18
\$\begingroup\$

You can determine the volume of objects based on a given set of dimensions:

  • The volume of a sphere can be determined using a single number, the radius (r)
  • The volume of a cylinder can be determined using two numbers, the radius (r) and the height (h)
  • The volume of a box can be determined using three numbers, the length (l), width (w) and the height (h)
  • The volume of an irregular triangular pyramid can be determined using four numbers, the side lengths (a, b, c) and the height (h).

The challenge is to determine the volume of an object given one of the following inputs:

  • A single number (r) or (r, 0, 0, 0) => V = 4/3*pi*r^3
  • Two numbers (r, h) or (r, h, 0, 0) => V = pi*r^2*h
  • Three numbers (l, w, h) or (l, w, h, 0) => V = l*w*h
  • Four numbers (a, b, c, h) => V = (1/3)*A*h, where A is given by Heron's formula: A = 1/4*sqrt((a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c))

Rules and clarifications:

  • The input can be both integers and/or decimals
  • You can assume all input dimensions will be positive
  • If Pi is hard coded it must be accurate up to: 3.14159.
  • The output must have at least 6 significant digits, except for numbers that can be accurately represented with fewer digits. You can output 3/4 as 0.75, but 4/3 must be 1.33333 (more digits are OK)
    • How to round inaccurate values is optional
  • Behaviour for invalid input is undefined
  • Standard rules for I/O. The input can be list or separate arguments

This is code golf, so the shortest solution in bytes win.

Test cases:

calc_vol(4)
ans =  268.082573106329

calc_vol(5.5, 2.23)
ans =  211.923986429533

calc_vol(3.5, 4, 5)
ans =  70

calc_vol(4, 13, 15, 3)
ans =  24

Related, but different.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Is the order of the dimensions required to be the order stated in the question? \$\endgroup\$
    – user45941
    Mar 8, 2016 at 23:33
  • \$\begingroup\$ Related \$\endgroup\$
    – Sp3000
    Mar 9, 2016 at 0:05
  • \$\begingroup\$ @Mego, you may choose... \$\endgroup\$ Mar 9, 2016 at 11:11
  • \$\begingroup\$ @StewieGriffin Varargs and getting at dynamically sized arrays is a pain in my language (at least for me, a beginner at it). Can I provide four functions to handle each arg count? \$\endgroup\$
    – cat
    Mar 16, 2016 at 22:45
  • \$\begingroup\$ You can have a fixed sized array with the last elements set to zero if need be. That should cover it I think? Or you can overload functions as in the Haskell answer. You can't have different functions with different names. \$\endgroup\$ Mar 16, 2016 at 22:52

12 Answers 12

8
\$\begingroup\$

Vitsy, 49 bytes

I thought you handed this one to me on a plate, but I found an unresolved bug to work around. Didn't hurt me, though.

lmN
3^43/*P*
2^*P*
**
v:++2/uV3\[V}-]V3\*12/^v*3/

Basically, with the input being a certain length for different functions, you spoon feed me my method syntax for doing this stuff. So, yay, success!

Explanation, one line at a time:

lmN
l   Get the length of the stack.
 m  Go to the line index specified by the top item of the stack (the length).
  N Output as a number.

3^43/*P*
3^
          Put to the power of 3.
  43/*    Multiply by 4/3.
      P*  Multiply by π

2^*P*
2^     Put to the second power.
  *    Multiply the top two items.
   P*  Multiply by π

**
**     Multiply the top three items of the stack.

v:++2/uV3\[V}-]V3\*12/^v*3/
v                            Save the top item as a temp variable.
 :                           Duplicate the stack.
  ++                         Sum the top three values.
    2/                       Divide by two.
      u                      Flatten the top stack to the second to top.
       V                     Capture the top item of the stack (semiperimeter) 
                             as a permanent variable.
        3\[   ]              Do the stuff in the brackets 3 times.
           V}-               Subtract the semiperimeter by each item.
               V             Push the global var again.
                3\*          Multiply the top 4 items.
                   12/^      Square root.
                       v*    Multiply by the temp var (the depth)
                         3/  Divide by three.

Input is accepted as command line arguments in the exact reverse as they appear in the question, with no trailing zeroes.

Try it online!

As an aside, here's something that's currently in development.

Java w/ Vitsy Package

Note that this package is work in progress; this is just to show how this will work in the future (documentation for this is not yet uploaded) and it is not golfed, and is a literal translation:

import com.VTC.vitsy;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;

public class Volume {
    public static void main(String[] args) {
        Vitsy vitsyObj = new Vitsy(false, true);
        vitsyObj.addMethod(new Vitsy.Method() {
            public void execute() {
                vitsyObj.pushStackLength();
                vitsyObj.callMethod();
                vitsyObj.outputTopAsNum();
            }
        });
        vitsyObj.addMethod(new Vitsy.Method() {
            public void execute() {
                vitsyObj.push(new BigDecimal(3));
                vitsyObj.powerTopTwo();
                vitsyObj.push(new BigDecimal(4));
                vitsyObj.push(new BigDecimal(3));
                vitsyObj.divideTopTwo();
                vitsyObj.multiplyTopTwo();
                vitsyObj.pushpi();
                vitsyObj.multiplyTopTwo();
            }
        });
        vitsyObj.addMethod(new Vitsy.Method() {
            public void execute() {
                vitsyObj.push(new BigDecimal(2));
                vitsyObj.powerTopTwo();
                vitsyObj.multiplyTopTwo();
                vitsyObj.pushpi();
                vitsyObj.multiplyTopTwo();
            }
        });
        vitsyObj.addMethod(new Vitsy.Method() {
            public void execute() {
                vitsyObj.multiplyTopTwo();
                vitsyObj.multiplyTopTwo();
            }
        });
        vitsyObj.addMethod(new Vitsy.Method() {
            public void execute() {
                vitsyObj.tempVar();
                vitsyObj.cloneStack();
                vitsyObj.addTopTwo();
                vitsyObj.addTopTwo();
                vitsyObj.push(new BigDecimal(2));
                vitsyObj.divideTopTwo();
                vitsyObj.flatten();
                vitsyObj.globalVar();
                vitsyObj.push(new BigDecimal(3));
                vitsyObj.repeat(new Vitsy.Block() {
                    public void execute() {
                        vitsyObj.globalVar();
                        vitsyObj.rotateRight();
                        vitsyObj.subtract();
                    }
                });
                vitsyObj.globalVar();
                vitsyObj.push(new BigDecimal(3));
                vitsyObj.repeat(new Vitsy.Block() {
                    public void execute() {
                        vitsyObj.multiplyTopTwo();
                    }
                });
                vitsyObj.push(new BigDecimal(1));
                vitsyObj.push(new BigDecimal(2));
                vitsyObj.divideTopTwo();
                vitsyObj.powerTopTwo();
                vitsyObj.tempVar();
                vitsyObj.multiplyTopTwo();
                vitsyObj.push(new BigDecimal(3));
                vitsyObj.divideTopTwo();
            }
        });
        vitsyObj.run(new ArrayList(Arrays.asList(args)));
    }
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Definitely the right tool for the job \$\endgroup\$
    – user45941
    Mar 8, 2016 at 23:48
5
\$\begingroup\$

C, 100 97 bytes

#define z(a,b,c,d) d?d*sqrt(4*a*a*b*b-pow(a*a+b*b-c*c,2))/12:c?a*b*c:3.14159*(b?a*a*b:4/3.*a*a*a)

edit 1: remove unnecessary decimal ., thanks Immibis!

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Can't 4./3. just be 4/3.? And can 2. and 12. just be 2 and 12? \$\endgroup\$
    – user253751
    Mar 9, 2016 at 0:07
  • \$\begingroup\$ You are absolutely correct. Thanks! \$\endgroup\$
    – Josh
    Mar 9, 2016 at 14:57
4
\$\begingroup\$

MATL, 57 53 51 44 bytes

3^4*3/YP*GpG1)*YP*GpG0H#)ts2/tb-p*X^3/*XhGn)

Input is an array with 1, 2, 3 or 4 numbers.

Try it online!

Explanation

Instead of using nested if loops, which is expensive in terms of bytes, this computes four possible results for any input, and then picks the appropriate result depending on input length.

When computing the results, even though only one of them needs to be valid, the others cannot give errors. This means for example that indexing the fourth element of the input is not allowed, because the input may have less than four elements.

                    % take input implicitly
3^4*3/YP*           % compute a result which is valid for length-1 input:
                    % each entry is raised to 3 and multiplied by 4/3*pi
G                   % push input
pG1)*YP*            % compute a result which is valid for length-2 input:
                    % product of all entries, times first entry, times pi
G                   % push input
p                   % compute a result which is valid for length-3 input:
                    % product of all entries
G                   % push input
0H#)ts2/tb-p*X^3/*  % compute a result which is valid for length-4 input:
                    % shorter version of Heron's formula applied on all
                    % entries except the last, times last entry, divided by 3
Xh                  % collect all results in a cell array
G                   % push input
n)                  % pick appropriate result depending on input length
                    % display implicitly
\$\endgroup\$
3
  • \$\begingroup\$ What rendition of Heron's formula are you using? \$\endgroup\$ Mar 9, 2016 at 23:13
  • \$\begingroup\$ @CoolestVeto The one with the semiperimeter. First formula from here \$\endgroup\$
    – Luis Mendo
    Mar 9, 2016 at 23:17
  • \$\begingroup\$ Well done @DonMuesli. I managed it using "only" 34 bytes more in MATLAB =) \$\endgroup\$ Mar 15, 2016 at 17:31
4
\$\begingroup\$

JavaScript ES6, 129 126 125 116 114 90 bytes

Saved lots of bytes (9) with a wonderful formula, thanks to Stewie Griffin! Since input must be nonzero, variable? will suffice for a defining-check.

with(Math){(a,b,c,h,Z=a*a)=>h?sqrt(4*Z*b*b-(D=Z+b*b-c*c)*D)/4:c?a*b*c:b?PI*Z*b:4/3*PI*Z*a}

Test it out!

with(Math){Q = (a,b,c,h,Z=a*a)=>h?sqrt(4*Z*b*b-(D=Z+b*b-c*c)*D)/4:c?a*b*c:b?PI*Z*b:4/3*PI*Z*a}
console.log = x => o.innerHTML += x + "<br>";

testCases = [[4], [5.5, 2.23], [3.5, 4, 5], [4, 13, 15, 3]];
redo = _ => (o.innerHTML = "", testCases.forEach(A => console.log(`<tr><td>[${A.join(", ")}]` + "</td><td> => </td><td>" + Q.apply(window, A) + "</td></tr>")));
redo();
b.onclick = _ => {testCases.push(i.value.split(",").map(Number)); redo();}
*{font-family:Consolas,monospace;}td{padding:0 10px;}
<input id=i><button id=b>Add testcase</button><hr><table id=o></table>

\$\endgroup\$
6
  • 5
    \$\begingroup\$ With math? Seems legit. \$\endgroup\$ Mar 8, 2016 at 20:34
  • \$\begingroup\$ This errors on Chrome 48, Uncaught SyntaxError: Unexpected token = (referring to Z=a*a) \$\endgroup\$ Mar 9, 2016 at 16:05
  • \$\begingroup\$ @PatrickRoberts Use Firefox. It allows for default parameters inside lambdas. \$\endgroup\$ Mar 9, 2016 at 16:17
  • \$\begingroup\$ I can't seem to get the 4-arg version to work... and you never use the value of h, which seems a bit of an oversight. \$\endgroup\$
    – Neil
    Mar 12, 2016 at 0:20
  • \$\begingroup\$ @Neil Huh, true. I'll need to see that formula again, Stewie deleted his comment... \$\endgroup\$ Mar 12, 2016 at 14:39
3
\$\begingroup\$

Haskell, 114 109 107 101 99 bytes

v[r]=4/3*pi*r^3
v[r,h]=pi*r^2*h
v[x,y,z]=x*y*z
v[a,b,c,h]=h/12*sqrt(4*a^2*b^2-(a^2+b^2-c^2)^2)

Takes a list of numbers and returns a volume. Call it like

v[7]

for a sphere, etc. The function is polymorphic for any type that implements sqrt (so, basically Float or Double).

It's not going to win any contests for brevity. But note how readable it is. Even if you don't really know Haskell, you can tell what it does quite easily. Haskell's pattern matching syntax makes it really easy to define strange functions that do something totally different depending on the shape of the input.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ (1/3)*(1/4)*h,,, why not h/12? Saves you a lot of bytes! \$\endgroup\$ Mar 9, 2016 at 11:00
  • 1
    \$\begingroup\$ Also, the variant of Heron's eq that Conor uses seems to be a lot shorter. \$\endgroup\$ Mar 9, 2016 at 11:16
  • \$\begingroup\$ @StewieGriffin Apparently yes. :-} \$\endgroup\$ Mar 9, 2016 at 12:09
  • \$\begingroup\$ Haskell is only readable for infix math, which I don't find readable. Then you get into . and # and $ and it becomes Mathematica Soup. \$\endgroup\$
    – cat
    Mar 17, 2016 at 15:33
3
\$\begingroup\$

PowerShell, 165 161 bytes

param($a,$b,$c,$h)((($h/12)*[math]::Sqrt(($a+$b+$c)*(-$a+$b+$c)*($a-$b+$c)*($a+$b-$c))),(($a*$b*$c),((($p=[math]::PI)*$b*$a*$a),($p*$a*$a*$a*4/3))[!$b])[!$c])[!$h]

So ... Many ... Dollars ... (31 of the 161 characters are $, for 19.25% of the code) ... but, saved 4 bytes thanks to Stewie Griffin!

We take in four inputs, and then progressively index into pseudo-ternary statements based on them in reverse order. E.g., the outside (..., ...)[!$h] tests whether the fourth input is present. If so, the !$h will equal 0 and the first half is executed (the volume of an irregular triagonal pyramid). Otherwise, !$h with $h = $null (as it's uninitialized) will equal 1, so it goes to the second half, which itself is a pseudo-ternary based on [!$c] and so on.

This is likely close to optimal, since the supposedly-shorter formula that (e.g.) Cᴏɴᴏʀ O'Bʀɪᴇɴ is using is actually 2 bytes longer in PowerShell thanks to the lack of a ^ operator ... The only real savings comes from (1/3)*(1/4)*A*$h golfing to A*$h/12, and setting $p later to save a couple bytes instead of the lengthy [math]::PI call.

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

CJam, 67 66 bytes

q~0-_,([{~3#4*P*3/}{~\_**P*}{:*}{)\a4*(a\[1W1]e!..*+::+:*mq*C/}]=~

I will work on shortening it soon. Try it online!

Explanation to come.

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

Seriously, 65 59 55 bytes

`kd@;Σ½╗"╜-"£Mπ╜*√*3@/``kπ``ª*╦*``3;(^/4*╦*`k,;lD(E@i(ƒ

Try it online!

Explanation

This one is a doozy. I'm going to break the explanation into multiple parts.

Main body:

`...``...``...``...`k,;lD(E@i(ƒ
`...``...``...``...`k            push 4 functions to a list
                     ,;lD        push input, len(input)-1
                         (E      get the function at index len(input)-1
                           @i(   flatten the input list
                              ƒ  execute the function

Function 0:

3;(^/4*╦*
3;(^       push 3, r^3
    /      divide (r^3/3)
     4*    multiply by 4 (4/3*r^3)
       ╦*  multiply by pi (4/3*pi*r^3)

Function 1:

ª*╦*
ª     r^2
 *    multiply by h (r^2*h)
  ╦*  multiply by pi (pi*r^2*h)

Function 2:

kπ  listify, product (l*w*h)

Function 3 (21 bytes; nearly half the program length!)

kd@;Σ½╗"╜-"£Mπ╜*√*3@/
kd@                    listify, dequeue h, bring [a,b,c] back on top
   ;Σ½                       dupe, sum, half (semiperimeter)
      ╗                push to register 0
       "╜-"£M          map: push s, subtract (s-x for x in (a,b,c))
             π         product
              ╜*√      multiply by s, sqrt (Heron's formula for area of the base)
                 *3@/  multiply by h, divide by 3 (1/3*A*h)
\$\endgroup\$
1
\$\begingroup\$

Matlab, 78 bytes

@(a,b,c,d)pi*a^2*(4/3*a*~b+b*~c)+a*b*c*~d+d/12*(4*a^2*b^2-(a^2+b^2-c^2)^2)^.5;

Quite sure it can't get any shorter than this. ~b, ~c and ~d, are 0 if each of the dimensions are non-zero. A formula with a zero dimension will just give zero. That way, each of the formulas can simply be summed. No if and else required.

Call it like this (or try it online here):

g=@(a,b,c,d)pi*a^2*(4/3*a*~b+b*~c)+a*b*c*~d+d/12*(4*a^2*b^2-(a^2+b^2-c^2)^2)^.5;

g(4,0,0,0)
ans =  268.082573106329

g(5.5,2.23,0,0)
ans =  211.923986429533

g(3.5,4,5,0)
ans =  70

g(4,13,15,3)
ans =  24
\$\endgroup\$
2
  • 1
    \$\begingroup\$ What a madness of variables :-) Yes, this seems hard to shorten any further \$\endgroup\$
    – Luis Mendo
    Mar 15, 2016 at 17:42
  • \$\begingroup\$ Maybe add a link to try it online? ideone.com/6VZF9z \$\endgroup\$
    – Luis Mendo
    Mar 15, 2016 at 17:44
0
\$\begingroup\$

Python 3 2, 127 119 116 bytes

Credit to somebody and Mego for all their help with golfing. Credit also to Cᴏɴᴏʀ O'Bʀɪᴇɴ and Josh as I borrowed parts of their answers for this one.

def v(a,b,c,d):z=a*a;P=3.14159;print filter(int,[max(0,(4*z*b*b-(z+b*b-c*c)**2))**.5*d/12,a*b*c,P*z*b,P*z*a*4/3])[0]

Ungolfed:

def v(a, b, c, d):
    z = a*a
    p = 3.14159
    s = filter(int,[max(0,(4*z*b*b-(z+b*b-c*c)**2))**.5*d/12,a*b*c,P*z*b,P*z*a*4/3])
    print s[0]
\$\endgroup\$
2
  • \$\begingroup\$ Golfed more: def v(a,b,c,d):z=a*a;p=355/113;return[x for x in[(4*z*b*b-(z+b*b-c*c)**2)**.5*d/12,a*b*c,p*z*b,p*z*a*4/3]if x][0], assuming input is padded with 0s. \$\endgroup\$
    – ASCII-only
    Mar 10, 2016 at 7:14
  • \$\begingroup\$ Also, if you use Python 2 instead, you can do def v(a,b,c,d):z=a*a;P=3.14159;return filter(int,[(4*z*b*b-(z+b*b-c*c)**2)**.5*d/12,a*b*c,P*z*b,P*z*a*4/3])[0] \$\endgroup\$
    – ASCII-only
    Mar 10, 2016 at 7:49
0
\$\begingroup\$

Mathematica, 114 (103)

Pure function: 114

Which[(l=Length@{##})<2,4.Pi/3#1^3,l<3,#1^2.Pi#2,l<4,#1#2#3,l<5,(4#1^2#2^2-(#1^2+#2^2-#3^2)^2)^.5#4/12]~Quiet~All&

Ungolfed:

fun = Which[
  (l = Length@{##}) < 2,
    4. Pi/3 #1^3,
  l < 3,
    #1^2 Pi #2, 
  l < 4,
    #1 #2 #3, 
  l < 5,
    (4 #1^2 #2^2 - (#1^2 + #2^2 - #3^2)^2)^.5 #4/12
]~Quiet~All &

Usage:

fun[4]
268.083
fun[5.5, 2.23]
211.924
fun[3.5, 4, 5]
70.
fun[4, 13, 15, 3]
24.

If named functions are allowed: 103

f[r_]:=4.Pi/3r^3
f[r_,h_]:=r^2.Pi h
f[l_,w_,h_]:=l w h
f[a_,b_,c_,h_]:=(4a^2b^2-(a^2+b^2-c^2)^2)^.5h/12

Usage:

f[4]
268.083
f[5.5, 2.23]
211.924
f[3.5, 4, 5]
70.
f[4, 13, 15, 3]
24.
\$\endgroup\$
2
  • 1
    \$\begingroup\$ #1==#, I think Quiet[x_]:=Quiet[x,All] and π (Alt-P on a Mac) fits in extended ASCII. \$\endgroup\$ Mar 10, 2016 at 21:11
  • \$\begingroup\$ Can't you replace #1 #2 #3 with 1##? Don't forget #==#1 \$\endgroup\$ Mar 15, 2016 at 18:30
0
\$\begingroup\$

Factor, 783 bytes

Well, this took forever.

USING: arrays combinators io kernel locals math math.constants math.functions quotations.private sequences sequences.generalizations prettyprint ;
: 1explode ( a -- x y ) dup first swap 1 tail ;
: 3explode ( a -- b c d ) 1explode 1explode 1explode drop ;
: spr ( r -- i ) first 3 ^ 4 3 / pi * swap * ;
: cyl ( r -- i ) 1explode 1explode drop 2 ^ pi swap * * ; : cub ( v -- i ) 1 [ * ] reduce ;
: A ( x a -- b d ) reverse dup dup dup 0 [ + ] reduce -rot 3explode neg + + -rot 3explode - + 3array swap 3explode + - 1array append 1 [ * ] reduce sqrt .25 swap * ;
: ilt ( a -- b c  ) V{ } clone-like dup pop swap A 1 3 / swap pick * * ;
: volume ( v -- e ) dup length { { [ 1 = ] [ spr ] } { [ 2 = ] [ cyl ] } { [ 3 = ] [ cub ] } { [ 4 = ] [ ilt ] } [ "bad length" throw ] } cond print ;

Call { array of numbers } volume .

\$\endgroup\$
1
  • \$\begingroup\$ @StewieGriffin :P I totally forgot to shorten the names of the functions. Won't help much, though. \$\endgroup\$
    – cat
    Mar 17, 2016 at 17:30

Your Answer

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

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