17
\$\begingroup\$

The challenge

This challenge is very straightforward. Given four 3-dimensional points, calculate the surface area of the tetrahedron that they form. This is , so shortest code wins. Standard loopholes apply, with the added stipulation that any built-in function to do this task given four points is prohibited.

You can assume all four points will be distinct, and will be given via STDIN, 1 point per line. Each point will consist of three 16-bit unsigned integers. The exact format of each point can be modified if it makes things easier, such as three space separated integers. Having each point on a separate line is mandatory however. Output should be through STDOUT, to at least 2 decimal places.

For those of you who do not know, a tetrahedron is a 3-d solid, formed by 4 triangular faces.

Example

# input (format is up to you, see clarification above)
[23822, 47484, 57901]
[3305, 23847, 42159]
[19804, 11366, 14013]
[52278, 28626, 52757]

# output
2932496435.95

Please leave a note if you notice my math is wrong.

\$\endgroup\$
5
  • \$\begingroup\$ @BetaDecay No, the idea is that they will be input via STDIN on four separate lines. I will edit the question to clarify this. \$\endgroup\$
    – stokastic
    Oct 3, 2014 at 18:18
  • \$\begingroup\$ Can the input be a [[list],[of],[lists]]? \$\endgroup\$
    – phosgene
    Oct 3, 2014 at 18:31
  • \$\begingroup\$ @phosgene I like to think reading the input is part of the challenge, so I'm going to say no. I will try to be more lenient with input specifications in future challenges. \$\endgroup\$
    – stokastic
    Oct 3, 2014 at 18:36
  • \$\begingroup\$ Is this a regular or irregular tetrahedron? \$\endgroup\$ Oct 3, 2014 at 19:24
  • \$\begingroup\$ @JamesWilliams the example posted is irregular. Your program should handle any input though, including regular tetrahedrons. \$\endgroup\$
    – stokastic
    Oct 3, 2014 at 19:25

9 Answers 9

5
\$\begingroup\$

Python, 198 178 161 chars

V=eval('input(),'*4)
A=0
for i in range(4):F=V[:i]+V[i+1:];a,b,c=map(lambda e:sum((a-b)**2for a,b in zip(*e)),zip(F,F[1:]+F));A+=(4*a*b-(a+b-c)**2)**.5
print A/4

The input format is as given in the question.

It calculates the length of the edges adjacent to each of the faces and then uses Heron's formula.

\$\endgroup\$
5
\$\begingroup\$

APL, 59

f←{+.×⍨⊃1 2-.⌽(⊂⍵)×1 2⌽¨⊂⍺}
.5×.5+.*⍨(f/2-/x),2f/4⍴x←⎕⎕⎕-⊂⎕

Works by calculating cross products

Explanation
The first line defines a function that takes two arguments (implicity named and ), implicitly expects them to be numerical arrays of length 3, treat them as 3d vectors, and calculates the squared magnitude of their cross product.

                        ⊂⍺   # Wrap the argument in a scalar
                   1 2⌽¨     # Create an array of 2 arrays, by rotating `⊂⍺` by 1 and 2 places
             (⊂⍵)×           # Coordinate-wise multiply each of them with the other argument
        1 2-.⌽               # This is a shorthand for:
        1 2  ⌽               #   Rotate the first array item by 1 and the second by 2
           -.                #   Then subtract the second from the first, coordinate-wise
       ⊃                     # Unwrap the resulting scalar to get the (sorta) cross product
   +.×                       # Calculate the dot product of that...
      ⍨                      # ...with itself
f←{+.×⍨⊃1 2-.⌽(⊂⍵)×1 2⌽¨⊂⍺} # Assign function to `f`

The second line does the rest.

                         ⎕⎕⎕-⊂⎕ # Take 4 array inputs, create an array of arrays by subtracting one of them from the other 3
                       x←        # Assign that to x
                     4⍴          # Duplicate the first item and append to the end
                  2f/            # Apply f to each consecutive pair
            2-/x                 # Apply subtraction to consecutive pairs in x
          f/                     # Apply f to the 2 resulting arrays
         (f/2-/x),2f/4⍴x←⎕⎕⎕-⊂⎕ # Concatenate to an array of 4 squared cross products
   .5+.*⍨                        # Again a shorthand for:
   .5  *⍨                        #   Take square root of each element (by raising to 0.5)
     +.                          #   And sum the results
.5×                              # Finally, divide by 2 to get the answer
\$\endgroup\$
3
  • \$\begingroup\$ If you are not sure whether it is hieroglyphs or a corrupted dll file it is probably gonna be APL. Could you perhaps explain somewhat more what some of those symbols do? It's not that I want to learn it but I am still rather intrigued by how you can program with those seemingly obscure symbols=P \$\endgroup\$
    – flawr
    Oct 6, 2014 at 16:55
  • \$\begingroup\$ @flawr I usually does that because golfing in APL mostly comes down to algorithm design and would most likely result in an uncommon approach to the problem. But I felt like "calculating cross product" conveys enough about the algorithm here. If you want a full-on explanation I will do it later today. \$\endgroup\$
    – TwiNight
    Oct 7, 2014 at 12:05
  • \$\begingroup\$ The idea of calculating the cross product was clear, but the code itself leaves me without any clue, so I just thought some few words about what parts of the code do what would be great, but of course I do not want to urge you to write a detailed explaination! \$\endgroup\$
    – flawr
    Oct 7, 2014 at 12:33
4
\$\begingroup\$

Matlab/Octave 103

I assume the values to be stored in the variable c. This uses the fact that the area of a triangle is the half length of the cross product of two of its side vectors.

%input
[23822, 47484, 57901;
3305, 23847, 42159;
19804, 11366, 14013;
52278, 28626, 52757]



%actual code
c=input('');
a=0;
for i=1:4;
    d=c;d(i,:)=[];
    d=d(1:2,:)-[1 1]'*d(3,:);
    a=a+norm(cross(d(1,:),d(2,:)))/2;
end
a
\$\endgroup\$
8
  • \$\begingroup\$ Each point must be entered on a separate line as standard input. \$\endgroup\$
    – DavidC
    Oct 4, 2014 at 1:30
  • \$\begingroup\$ I first thought there is no such thing as standard input in Matlab, but I discovered a function that can be used to simulate this via the command window, so now you can pass the input as you could in other languages. \$\endgroup\$
    – flawr
    Oct 4, 2014 at 9:18
  • \$\begingroup\$ Interesting. That's the same command that Mathematica uses, Input[] \$\endgroup\$
    – DavidC
    Oct 4, 2014 at 11:59
  • \$\begingroup\$ Why do you think that this is interesting? 'input' seems to me like a pretty generic name for a function that does this. \$\endgroup\$
    – flawr
    Oct 4, 2014 at 12:12
  • \$\begingroup\$ Until yesterday, I didn't really know what "standard input" meant, and I thought that Mathematica did not have "standard" input, even though I had regularly used Input[], InputString[], Import[], and ImportString[]. \$\endgroup\$
    – DavidC
    Oct 4, 2014 at 12:31
3
\$\begingroup\$

Python 3, 308 298 292 279 258 254

from itertools import*
def a(t,u,v):w=(t+u+v)/2;return(w*(w-t)*(w-u)*(w-v))**.5
z,x,c,v,b,n=((lambda i,j:(sum((i[x]-j[x])**2for x in[0,1,2]))**.5)(x[0],x[1])for*x,in combinations([eval(input())for i in">"*4],2))
print(a(z,x,v)+a(z,c,b)+a(b,v,n)+a(x,c,n))

This uses:

  • The Pythagorean Theorem (in 3D) to work out the length of each line
  • Heron's Formula to work out the area of each triangle
\$\endgroup\$
5
  • 1
    \$\begingroup\$ I used the same method for testing my solution. I'll have to try golfing mine and post it later. \$\endgroup\$
    – stokastic
    Oct 3, 2014 at 19:54
  • 1
    \$\begingroup\$ Your for i in">"*4 is clever \$\endgroup\$
    – stokastic
    Oct 3, 2014 at 19:58
  • \$\begingroup\$ You can hard code a length of 3, instead of using len(i) in your range function. \$\endgroup\$
    – stokastic
    Oct 3, 2014 at 20:28
  • 1
    \$\begingroup\$ You could save a few more characters doing the square root as x**0.5, instead of math.sqrt(x). \$\endgroup\$ Oct 3, 2014 at 22:46
  • 1
    \$\begingroup\$ You can save two bytes by putting def a(t,u,v) on one line like so: def a(t,u,v):w=(t+u+v)/2;return(w*(w-t)*(w-u)*(w-v))**0.5. \$\endgroup\$
    – Beta Decay
    Oct 4, 2014 at 8:50
2
\$\begingroup\$

Mathematica 168 154

This finds the lengths of the edges of the tetrahedron and uses Heron's formula to determine the areas of the faces.

t = Subsets; p = Table[Input[], {4}];
f@{a_, b_, c_} := Module[{s = (a + b + c)/2}, N[Sqrt[s (s - #) (s - #2) (s -#3)] &[a, b, c], 25]]
  Tr[f /@ (EuclideanDistance @@@ t[#, {2}] & /@ t[p, {3}])]

There is a more direct route that requires only 60 chars, but it violates the rules insofar as it computes the area of each face with a built-in function, Area:

p = Table[Input[], {4}];
N[Tr[Area /@ Polygon /@ Subsets[p, {3}]], 25]
\$\endgroup\$
1
\$\begingroup\$

Sage – 103

print sum((x*x*y*y-x*y*x*y)^.5for x,y in map(differences,Combinations(eval('vector(input()),'*4),3)))/2

The input-reading part is adapted from Keith Randall’s answer.

\$\endgroup\$
1
\$\begingroup\$

R, 96 80 bytes

\(x)sum(combn(1:4,3,\(i)(4*(k<-dist(x[i,])^2)[1]*k[2]-(k[1]+k[2]-k[3])^2)^.5))/4

Attempt This Online!

-16 bytes were removed thanks to pajonk

It is quite simple, but still I am glad my answer is shorter than the others here except APL =)

combn(1:4,3) gives the vertices of 4 triangles of a tetrahedron (as indices of our input object):

     [,1] [,2] [,3] [,4]
[1,]    1    1    1    2
[2,]    2    2    3    3
[3,]    3    4    4    4

Then we iterate through every column, calculate squares of distances with a builtin dist and ^2, apply the Heron's formula to every combination and sum all areas.

\$\endgroup\$
3
  • \$\begingroup\$ combn has FUN argument, which is handy for golfing, here -14 bytes. \$\endgroup\$
    – pajonk
    2 days ago
  • \$\begingroup\$ Actually -16 bytes, because one pair of parentheses fewer :) \$\endgroup\$
    – pajonk
    2 days ago
  • \$\begingroup\$ @pajonk that's pretty useful; i imagine that another 6 bytes could be golfed away if the type of the input object x would allow this: combn(x,3,\(i)(4*(k<-dist(i)^2)[1]*k[2]-(k[1]+k[2]-k[3])^2)^.5) - but this does not work for a list \$\endgroup\$ yesterday
0
\$\begingroup\$

Python - 260

I'm not sure what the etiquette on posting answers to your own questions is, but her is my solution, which I used to verify my example, golfed:

import copy,math
P=[input()for i in"1234"]
def e(a, b):return math.sqrt(sum([(b[i]-a[i])**2 for i in range(3)]))
o=0
for j in range(4):p=copy.copy(P);p.pop(j);a,b,c=[e(p[i],p[(i+1)%3])for i in range(3)];s=(a+b+c)/2;A=math.sqrt(s*(s-a)*(s-b)*(s-c));o+=A
print o

It uses the same procedure as laurencevs.

\$\endgroup\$
2
  • 4
    \$\begingroup\$ As a rule of thumb, it's a best idea to wait a few days before answering your own question, especially if your score is low, in order to not cool down the motivation of the viewers. \$\endgroup\$
    – Blackhole
    Oct 3, 2014 at 21:09
  • \$\begingroup\$ A few tips: You can save some characters by r=range. lambda is shorter than def. math.sqrt can be replaced by (…)**.5. p=copy.copy(P);p.pop(j); can be shortened to p=P[:j-1]+P[j:]. A is only used once. \$\endgroup\$
    – Wrzlprmft
    Oct 8, 2014 at 15:34
0
\$\begingroup\$

C, 303

Excluding unnecessary whitespace. However, there's still a lot of golfing to be done here (I will try to come back and do it later.) It's the first time I've declared a for loop in a #define. I've always found ways to minmalise the number of loops before.

I had to change from float to double to get the same answer as the OP for the test case. Before that, it was a round 300.

scanf works the same whether you separate your input with spaces or newlines, so you can format it into as many or as few lines as you like.

#define F ;for(i=0;i<12;i++)
#define D(k) (q[i]-q[(i+k)%12])
double q[12],r[12],s[4],p,n;

main(i){
  F scanf("%lf",&q[i])
  F r[i/3*3]+=D(3)*D(3),r[i/3*3+1]+=D(6)*D(6)
  F r[i]=sqrt(r[i])
  F i%3||(s[i/3]=r[(i+3)%12]/2),s[i/3]+=r[i]/2
  F i%3||(p=s[i/3]-r[(i+3)%12]),p*=s[i/3]-r[i],n+=(i%3>1)*sqrt(p)   
  ;printf("%lf",n);       
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.