箱の外側を考える


16

球体を5面のボックスに収めようとしていますが、完全に収まらない場合があります。球体がボックスの外側(リムの上)にどれだけあるかを計算する関数を作成します。

次の3つの状況が考えられます。

  • 球体は完全にボックスに収まります。答えは0です。
  • 球は箱の縁にあります。答えは、総量の半分以上になります。
  • 球体は箱の底にあります。

ここで各状況を見ることができます:

画像

この値を少なくとも4桁の有効数字で計算するプログラムまたは関数を作成する必要があります。

入力:便利な形式の4つの非負の実数*-幅、長さ、ボックスの深さ(内部測定)、および球体の直径。

出力:使用可能な形式の1つの非負の実数*-ボックスの外側の球の総体積(パーセントではありません)。

* 10進数文字列との間で変換可能である必要があります

できるだけ三角法の使用を制限することをお勧めします。

これは人気コンテストなので、箱の外で考えてください!


例を挙げてください。
mniip 14年

1
我々は仮定することができますいずれかのボックスの壁が薄い無限にあるか、与えられた寸法は内部寸法は?:)
ダレンストーン14年

入力の最大値は何ですか?
ブレンダー14年

@DarrenStone壁の厚さは重要ではないと思います。無限と考えることもできます。そのため、ボックスは無限ブロックの長方形の穴になります。結果は、壁の厚さの他の値と同じになります。箱や球体を物理的に壊したり、変形したり、スライスしたりして、ルールを曲げたりチートしたりする場合、または本当に奇妙なことをする場合を除きます。
ビクターStafusa 14年

3
@DarrenStoneボックスには、見栄えを良くするためだけの厚さがあります。この問題は内部寸法を扱います。
ケンドールフレイ14年

回答:


21

前方へ

下のボックスの外の球を見つけてください。

「球体」は体積計算機能fです。参照テストケースは「ボックス」を構成します。

                     ( x y z d -- v )
                 : f { F: z F: d } d f2/ 
              { F: r } fmin { F: m } m f2/ {
             F: b } d m f<= d z f<= and if 0e
             else r r r f* b b f* f- fsqrt f-
              { F: t } d m f<= t z f> or if d 
               z f- else d t f- then r 3e f* 
                  fover f- pi f* fover f*
                      f* 3e f/ then ;

                     1e                 1e      
                     1e                 1e 
                     f                  f. 
            cr       1e        1e       0e      
            1e       f         f.       cr 
            1e       1e 0.5e 1e f f. cr 1e 
            0.999e 1e          1e     f  
            f.  cr            0.1e 1e   
            1.000e 0.500e f f. cr

出力:

0. 
0.523598775598299 
0.261799387799149 
0.279345334323962 
0.0654299441440212 

5

Java-整数ベース

このプログラムはpiを使用せず、外部関数(sqrtでさえも)を呼び出しません。-それだけで単純な算術を使用して+-*/。さらに、スケーリング手順以外に、整数のみで機能します。基本的に球体を小さな立方体に分割し、箱の外側にあるものを数えます。

public class Box {
    private static final int MIN = 10000;
    private static final int MAX = MIN * 2;

    private static final int[] SQ = new int[MAX * MAX + 1];

    static {
        int t = 1;
        for (int i = 1; i <= MAX; ++i) {
            while (t < i * i) SQ[t++] = i - 1;
        }
        SQ[MAX * MAX] = MAX;
    }

    public static long outsideInt(int r, int w, int z) {
        int r2 = r * r;
        int o = z - r + 1;
        if (w < r * 2) {
            int t = 1 - SQ[r2 - w * w / 4];
            if (t < o) o = t;
        }
        long v = 0;
        for (int i = o; i <= r; ++i) {
            int d = r2 - i * i;
            int j0 = SQ[d];
            v += 1 + 3 * j0;
            for (int j = 1; j <= j0; ++j)
                v += 4 * SQ[d - j * j];
        }
        return v;
    }

    public static double outside(double x, double y, double z, double d) {
        double f = 1;
        double w = x < y ? x : y;
        double r = d / 2;
        while (r < MIN) {
            f *= 8;
            r *= 2;
            w *= 2;
            z *= 2;
        }
        while (r > MAX) {
            f /= 8;
            r /= 2;
            w /= 2;
            z /= 2;
        }
        return outsideInt((int) r, (int) w, (int) z) / f;
    }

    public static void main(final String... args) {
        System.out.println(outside(1, 1, 1, 1));
        System.out.println(outside(1, 1, 0, 1));
        System.out.println(outside(1, 1, 0.5, 1));
        System.out.println(outside(1, 0.999, 1, 1));
        System.out.println(outside(0.1, 1, 1, 0.5));
    }
}

出力:

0.0
0.5235867850933005
0.26178140856157484
0.27938608275528054
0.06542839088004015

この形式では、プログラムは2GB以上のメモリを必要とし(-Xmx2300mここで動作します)、かなり遅いです。メモリを使用して、平方根の束を(計算により)事前計算します。本当に必要なわけではありませんが、それなしではLOTが遅くなります。メモリのニーズと速度の両方を改善するには、MIN定数の値を減らします(ただし、精度は低下します)。


2

Python 2(配列ベースのアプローチ)

そのグリッドの特定の正方形が円の内側または円の外側にある場合、真理値を持つ配列の配列を作成します。円を大きくすると、より正確になります。次に、特定の行の下または上の領域を選択し、円に属する正方形の数をカウントし、それを円全体にある正方形の量で割ります。

import math as magic
magic.more = magic.pow
magic.less = magic.sqrt

def a( width, length, depth, diameter ):
  precision = 350 #Crank this up to higher values, such as 20000

  circle = []
  for x in xrange(-precision,precision):
    row = []
    for y in xrange(-precision,precision):
      if magic.less(magic.more(x, 2.0)+magic.more(y, 2.0)) <= precision:
        row.append(True)
      else:
        row.append(False)
    circle.append(row)

  if min(width,length,depth) >= diameter:
    return 0
  elif min(width,length) >= diameter:
    row = precision*2-int(precision*2*float(depth)/float(diameter))
    total = len([x for y in circle for x in y if x])
    ammo = len([x for y in circle[:row] for x in y if x])
    return float(ammo)/float(total)
  else:
    #Why try to fit a sphere in a box if you can try to fit a box on a sphere
    maxwidth = int(float(precision*2)*float(min(width,length))/float(diameter))
    for row in xrange(0,precision*2):
      rowwidth = len([x for x in circle[row] if x])
      if rowwidth > maxwidth:
        total = len([x for y in circle for x in y if x])
        ammo = len([x for y in circle[row:] for x in y if x])
        return float(ammo)/float(total)

2

Python 2.7、球面キャップ式

このバージョンは、場合によっては実行時警告をスローしますが、正しい答えを出力します。

import numpy as n
x,y,z,d=(float(i) for i in raw_input().split(' '))
r=d/2
V=4*n.pi*r**3/3
a=n.sqrt((d-z)*z)
b=min(x,y)/2
h=r-n.sqrt(r**2-b**2)
c=lambda A,H: (3*A**2+H**2)*n.pi*H/6
print(0 if d<=z and r<=b else c(a,d-z) if r<=b and z>r else V-c(a,z) if r<=b or z<h else V-c(b,h))

11文字以上の場合、警告を取り除くことができます。

import math as m
x,y,z,d=(float(i) for i in raw_input().split(' '))
r=d/2
V=4*m.pi*r**3/3
if d>z:
    a=m.sqrt((d-z)*z)
b=min(x,y)/2
h=r-m.sqrt(r**2-b**2)
c=lambda A,H: (3*A**2+H**2)*m.pi*H/6
print(0 if d<=z and r<=b else c(a,d-z) if r<=b and z>r else V-c(a,z) if r<=b or z<h else V-c(b,h))

バージョン1で実行されるテストケースは次のとおりです。

$ python spherevolume.py
1 1 1 1
0
$ python spherevolume.py
1 1 0 1
0.523598775598
$ python spherevolume.py
1 1 .5 1
0.261799387799
$ python spherevolume.py
1 .999 1 1        
0.279345334324
$ python spherevolume.py
.1 1 1 0.5
spherevolume.py:65: RuntimeWarning: invalid value encountered in sqrt
  a=n.sqrt((d-z)*z) or b
0.065429944144

これはコードゴルフではありませんが、コードのすべてを短縮import numpy as nして削除することができます。from numpy import*n.
ティムテック14年

@Timtechヘッドアップと提案をありがとう。
user2487951 14年

1

Mathematica

適切な制限のある数値積分を使用します。

f[width_, length_, height_, diam_] := 
 With[{r = diam/2, size = Min[width, length]/2},
  Re@NIntegrate[
    Boole[x^2 + y^2 + z^2 < r^2], {x, -r, r}, {y, -r, r}, 
      {z, -r, Max[-r, If[size >= r, r - height, Sqrt[r^2 - size^2]]]}]
  ]

0

リファレンス実装-C#

using System;

namespace thinkoutsidethebox
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(OutsideTheBox(1, 1, 1, 1));
            Console.WriteLine(OutsideTheBox(1, 1, 0, 1));
            Console.WriteLine(OutsideTheBox(1, 1, 0.5, 1));
            Console.WriteLine(OutsideTheBox(1, 0.999, 1, 1));
            Console.WriteLine(OutsideTheBox(0.1, 1, 1, 0.5));
        }

        static double OutsideTheBox(double x, double y, double z, double d)
        {
            x = Math.Min(x, y);
            double r = d / 2; // radius
            double xr = x / 2; // box 'radius'
            double inside = 0; // distance the sphere sits inside the box
            if (d <= x && d <= z) // it fits
            {
                return 0;
            }
            else if (d <= x || r - Math.Sqrt(r * r - xr * xr) > z) // it sits on the bottom
            {
                inside = z;
            }
            else // it sits on the rim
            {
                inside = r - Math.Sqrt(r * r - xr * xr);
            }
            // now use the formula from Wikipedia
            double h = d - inside;
            return (Math.PI * h * h / 3) * (3 * r - h);
        }
    }
}

出力:

0
0.523598775598299
0.261799387799149
0.279345334323962
0.0654299441440212

これらの結果がわかりません。最初のボールは明らかに0です。2番目のボールは高さがないため、1にする必要があります。ケース4の箱は少し小さいため、箱の上にあります。答えは0.5より少し大きいはずです。幅/長さはボールを内側に収めるのに十分ではないため、最後の答えは> 0.5でなければなりません。
Sumurai8

@ Sumurai8「出力:箱の外側の球の総体積(パーセンテージではない)。」
ケンドールフレイ14年

0

ルビー

見てみましょう...
ボックスが完全に内側にある場合、width> diameter; 長さ>直径および高さ>直径。
これが実行する最初のチェックになります。

下部にある場合はw> d; l> d and h V=(pi*h^2 /3)*(3r-h)そのため、その場合、高さを取得してそれを実行します。

スタックしている場合は、同様の式(V=(pi*h/6)*(3a^2 + h^2))を使用します。実際、以前の式はこれに基づいています!基本的に、これを使用します。aは、wとlの小さい方です。(ヒント、実行することで高さを取得できますh=r-a

これでコード!

def TOTB(wi,le,hi,di)
  if wi>=di and le>=di and hi>=di
    res = 0
  elsif wi>=di and le>=di
    r = di/2
    res = 3*r
    res -= hi
    res *= Math::PI
    res *= hi*hi
    res /= 3
  else
    r = di/2
    if wi>le
      a=le
    else
      a=wi
    end #had issues with the Ternary operator on ruby 2.2dev
    h = r-a
    res = 3*a*a
    res += h*h
    res *= Math::PI
    res *= h
    res /= 6
  end
  res
end

注**私はそれをあまりテストしなかったので、誰かが気づいたなら、エラーが入り込んだかもしれません!
しかし、数学はしっかりしています。
短いバージョン:

v1 = ->r,h{(3*r -h)*Math::PI*h*h/3}
v2 = ->r,a{h=r-a;((3*a*a)+(h*h))*h*Math::PI/6}
TOTB = ->wi,le,hi,di{(di<wi&&di<le&&di<hi)?0:((di<wi&&di<le)?v1[di/2,hi]:v2[di/2,((wi>le)?le:wi)])}

(v2でhを取得する方法が異なることは確かですが、後で修正します。


いいね そのコードははっきりと読みます。しかし、あなたは次の声明について確かですか?「私たちは高さを得ることができますh=r-a」私はちょうど球冠式を読んでいたが、はそれほど単純な関係を示唆していません。もう一度読みます。
ダレンストーン14年

@DarrenStone今振り返ってみると、よく分からない。私は非常にダウン/疲れ果てていますが、どちらにしても、パッチを当てるのはとても簡単です!

a = wi > le ? le : wi動作するはずです。そうでなければ、バグがあります。
コンラッドボロスキー14年

a = wi>le?le:wi動作しませんでした。私はgit ruby​​(2.2開発者)を実行しているためだと推測しています。

0

C ++

#define _USE_MATH_DEFINES   //so I can use M_PI
#include <math.h>           //so I can use sqrt()
#include <iostream>
#include <algorithm>

using namespace std;


int main()
{
    double w;
    double l;
    double d;
    double sd;
    double min_wl;
    double pdbd;
    double sl;
    cin >> w >> l >> d >> sd;

    min_wl = min(w, l);
    if(sd <= min_wl)
    {
        pdbd = 0.0;
    } else
    {
        pdbd = (sqrt((((sd/2)*(sd/2))-((min_wl/2)*(min_wl/2)))) + (sd/2));
    }
    sl = sd - d;

    if(sd <= min(min_wl, d))
    {
        cout << 0;
        return 0;
    } else if((sl < pdbd) && (pdbd > 0.0))    //sits on lip of box
    {
        cout << (M_PI * (((sd/2) * pdbd * pdbd) - ((pdbd * pdbd * pdbd)/(3))));
        return 0;
    } else                  //sits on bottom of box
    {
        cout << (M_PI * (((sd/2) * sl * sl)-((sl * sl * sl)/(3))));
        return 0;
    }
    return 0;
}

私のコードは、半円の一部のグラフの回転ソリッドの体積を見つけます。 pdbd球の表面上の点の投影の直線距離を、延長すると箱の底に垂直になる球の直径まで保持します。含む2つの式はM_PI基本的に一体の抗誘導体であるpi * -(x^2)+2rxXに対していずれかで評価(xは上述直径上記球スルーとに沿った長さの尺度であり、rは球の半径である)、pdbdまたは異なる直径で発生する特定のケースに応じた、球体の直径と箱の深さの差。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.