in andere Richtungen denken

16

Sie versuchen, eine Kugel in eine 5-seitige Box einzupassen, aber manchmal passt sie nicht vollständig. Schreiben Sie eine Funktion, um zu berechnen, wie viel von der Kugel außerhalb (über dem Rand) der Box liegt.

Es gibt 3 mögliche Situationen:

  • Die Kugel passt vollständig in die Schachtel. Die Antwort wird 0 sein.
  • Die Kugel sitzt am Rand der Schachtel. Die Antwort wird mehr als die Hälfte des Gesamtvolumens sein.
  • Die Kugel sitzt auf dem Boden der Kiste.

Sie können jede Situation hier sehen:

Bild

Sie müssen ein Programm oder eine Funktion schreiben, um diesen Wert mit mindestens 4 signifikanten Stellen zu berechnen.

Eingabe: 4 nicht negative reelle Zahlen in beliebigem Format * - Breite, Länge, Tiefe der Box (Innenmaße) und Durchmesser der Kugel.

Ausgabe: 1 nicht negative reelle Zahl in einem verwendbaren Format * - das Gesamtvolumen (nicht der Prozentsatz) der Kugel außerhalb der Box.

* muss in / aus einer Dezimalzeichenfolge konvertierbar sein

Es wird empfohlen, die Verwendung der Trigonometrie so weit wie möglich zu beschränken.

Dies ist ein Beliebtheitswettbewerb, denken Sie also über den Tellerrand hinaus!

Kendall Frey
quelle
Irgendwelche Beispielfälle bitte?
4.
1
Können wir annehmen, dass entweder die Wände des Kastens unendlich dünn sind oder die angegebenen Maße Innenmaße sind? :)
Darren Stone
Was sind die Maximalwerte für die Eingänge?
Blender
@ DarrenStone Ich denke, dass die Wandstärken unwichtig sind. Sie könnten es auch als unendlich betrachten, so dass die Box ein rechteckiges Loch in einem unendlichen Block wäre. Das Ergebnis wäre dasselbe wie jeder andere Wert für die Wandstärke. Außer wenn Sie beabsichtigen, die Regeln zu biegen oder zu betrügen, indem Sie entweder die Box oder die Kugel physisch brechen, verzerren oder in Scheiben schneiden oder etwas wirklich Merkwürdiges tun.
Victor Stafusa
3
@ DarrenStone Die Boxen haben nur eine Dicke zum Zwecke eines schönen Bildes. Das Problem betrifft die Innenabmessungen.
Kendall Frey

Antworten:

21

Forth

Unten finden Sie eine Kugel außerhalb des Kastens.

Die "Kugel" ist die Volumenberechnungsfunktion f. Die Referenz-Testfälle bilden die "Box".

                     ( 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

Ausgabe:

0. 
0.523598775598299 
0.261799387799149 
0.279345334323962 
0.0654299441440212 
Darren Stone
quelle
5

Java - ganzzahlig

Dieses Programm verwendet kein pi und ruft keine externe Funktion auf - nicht einmal sqrt. Es verwendet nur einfache Arithmetik - +, -, *und /. Abgesehen von einem Skalierungsschritt wird ausschließlich mit ganzen Zahlen gearbeitet. Im Grunde teilt es die Kugel in kleine Würfel und zählt diejenigen, die sich außerhalb des Kastens befinden.

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));
    }
}

Ausgabe:

0.0
0.5235867850933005
0.26178140856157484
0.27938608275528054
0.06542839088004015

In dieser Form benötigt das Programm mehr als 2 GB Speicher (funktioniert -Xmx2300mhier) und ist ziemlich langsam. Es verwendet den Speicher, um eine Reihe von Quadratwurzeln (arithmetisch) vorab zu berechnen. es ist nicht wirklich notwendig, aber ohne das wäre es viel langsamer. Verringern Sie den Wert der MINKonstanten, um sowohl den Speicherbedarf als auch die Geschwindigkeit zu verbessern (dies verringert jedoch die Genauigkeit).

aditsu
quelle
2

Python 2 (Array-basierter Ansatz)

Es wird ein Array von Arrays mit Wahrheitswerten erstellt, wenn sich ein bestimmtes Quadrat in diesem Raster innerhalb oder außerhalb des Kreises befindet. Je größer der Kreis ist, den Sie zeichnen, desto präziser sollte es werden. Es wählt dann entweder einen Bereich unter oder über einer bestimmten Reihe aus und zählt die Anzahl der Quadrate, die zum Kreis gehören, und dividiert diese durch die Anzahl der Quadrate, die sich im gesamten Kreis befinden.

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)
Sumurai8
quelle
2

Python 2.7, Kugelkalottenformel

Diese Version gibt in einigen Fällen eine Laufzeitwarnung aus, gibt jedoch weiterhin die richtige Antwort aus.

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))

Für 11 Zeichen mehr kann ich die Warnung loswerden.

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))

Hier sind die auf Version 1 ausgeführten Testfälle:

$ 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
user2487951
quelle
Auch wenn dies nicht Code Golf ist, können Sie verkürzen import numpy as nzu from numpy import*und alle die wegzunehmen n.in Ihrem Code.
Timtech
@ Timtech Vielen Dank für die Köpfe und Anregungen.
user2487951
1

Mathematica

Verwenden der numerischen Integration mit geeigneten Grenzen.

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]]]}]
  ]
swish
quelle
0

Referenzimplementierung - 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);
        }
    }
}

Ausgabe:

0
0.523598775598299
0.261799387799149
0.279345334323962
0.0654299441440212
Kendall Frey
quelle
Ich verstehe diese Ergebnisse nicht. Der erste ist offensichtlich 0. Der zweite hat keine Höhe, so dass man 1 sein sollte. Der dritte kann den Ball aufnehmen, und genau die Hälfte davon ist darüber (die Antwort sollte 0,5 sein). Die Schachtel in Schachtel 4 ist etwas zu klein, sodass sie oben auf der Schachtel liegt. Die Antwort sollte etwas mehr als 0,5 sein. Die Antwort für die letzte sollte> 0,5 sein, da die Breite / Länge nicht ausreicht, um den Ball hinein zu passen.
Sumurai8
@ Sumurai8 "Ausgabe: das Gesamtvolumen ( nicht der Prozentsatz ) der Kugel außerhalb der Box."
Kendall Frey
0

Rubin

Mal sehen ...
Wenn die Box ganz innen ist, dann Breite> Durchmesser; Länge> Durchmesser und Höhe> Durchmesser.
Das sollte die erste Prüfung sein, die ausgeführt wird.

Wenn es unten sitzt, dann ist w> d; l> d und h V=(pi*h^2 /3)*(3r-h)Also in diesem Fall bekommen wir einfach die Höhe und lassen sie durchlaufen.

Wenn es nicht weiter geht, verwenden wir eine ähnliche Formel ( V=(pi*h/6)*(3a^2 + h^2)). Tatsächlich basiert unsere frühere Formel auf dieser! Im Wesentlichen verwenden wir das, und a ist einfach das kleinere von w und l. (Hinweis, wir können Höhe bekommen, indem wir tun h=r-a)

Nun der Code!

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

Hinweis ** Ich habe es nicht zu oft getestet, daher ist möglicherweise ein Fehler aufgetreten. Wenn jemand einen bemerkt, sagen Sie es!
Die Mathematik ist jedoch solide.
Kürzere Version:

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)])}

(Jetzt weiß ich sicher, dass es anders ist, h für v2 zu bekommen, aber ich werde es später beheben.


quelle
Nett. Dieser Code liest sich deutlich. Aber sind Sie sich bei der folgenden Aussage sicher? "Wir können Höhe gewinnen, indem wir h=r-a" Ich habe gerade über die Kugelkalottenformeln nachgelesen , und das Diagramm legt keine so einfache Beziehung nahe. Ich werde es noch einmal lesen.
Darren Stone
@ DarrenStone Jetzt, da ich zurückblicke, bin ich mir nicht sicher. Ich bin außerordentlich niedergeschlagen / erschöpft, aber so oder so ist es sehr einfach zu patchen!
Ich bin mir fast sicher, a = wi > le ? le : widass es funktionieren sollte. Ansonsten hast du einen Bug.
Konrad Borowski
a = wi>le?le:wifunktioniert nicht. Ich vermute, es liegt daran, dass ich Git Ruby (2.2 Entwickler) verwende, es könnte ein Ungleichgewicht besagt haben.
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;
}

Mein Code ermittelt das Volumen des Rotationskörpers des Graphen eines Teils eines Halbkreises. pdbdHält den linearen Abstand der Projektion eines Punktes auf der Oberfläche der Kugel, der die Lippe des Kastens berührt, zum Durchmesser der Kugel, der im ausgefahrenen Zustand normal zum Boden des Kastens ist. Die zwei Ausdrücke, die enthalten, M_PIsind im Grunde genommen die Anti-Ableitung des Integrals von pi * -(x^2)+2rxin Bezug auf x (wobei x ein Maß für die Länge entlang des oben erwähnten Durchmessers durch die Kugel ist und wobei r der Radius der Kugel ist), ausgewertet entweder bei pdbdoder Der Unterschied zwischen Kugeldurchmesser und Kastentiefe hängt vom jeweiligen Fall ab, der bei den verschiedenen Abmessungen auftritt.

user3142682
quelle