Myśleć poza szablonowo

16

Próbujesz dopasować kulę do 5-stronnego pudełka, ale czasami nie pasuje ona całkowicie. Napisz funkcję, aby obliczyć, ile kuli znajduje się na zewnątrz (powyżej krawędzi) pudełka.

Istnieją 3 możliwe sytuacje:

  • Kula pasuje całkowicie do pudełka. Odpowiedź będzie wynosić 0.
  • Kula znajduje się na krawędzi pudełka. Odpowiedź będzie stanowić ponad połowę całkowitego wolumenu.
  • Kula znajduje się na dole pudełka.

Tutaj możesz zobaczyć każdą sytuację:

Wizerunek

Musisz napisać program lub funkcję, aby obliczyć tę wartość na co najmniej 4 cyfry znaczące.

Dane wejściowe: 4 nieujemne liczby rzeczywiste w dowolnym dogodnym formacie * - szerokość, długość, głębokość pudełka (wymiary wewnętrzne) i średnica kuli.

Wyjście: 1 nieujemna liczba rzeczywista w użytecznym formacie * - całkowita objętość (nie procent) kuli poza polem.

* musi być konwertowany na / z łańcucha dziesiętnego

Zachęcamy do maksymalnego ograniczenia używania trygonometrii.

To konkurs popularności, więc pomyśl nieszablonowo!

Kendall Frey
źródło
jakieś przykłady przypadków proszę?
mniip
1
Czy możemy założyć , że ściany pudełka są nieskończenie cienkie, czy podane wymiary to wymiary wewnętrzne? :)
Darren Stone
Jakie są maksymalne wartości dla wejść?
Blender
@DarrenStone Myślę, że grubość ścian jest nieistotna. Można to również uznać za nieskończone, aby pudełko było prostokątnym otworem w nieskończonym bloku. Wynik byłby taki sam jak każda inna wartość grubości ściany. Z wyjątkiem sytuacji, gdy zamierzasz nagiąć / oszukać reguły, fizycznie łamiąc, zniekształcając lub kroiąc pole lub kulę, lub robiąc coś naprawdę dziwnego.
Victor Stafusa,
3
@DarrenStone Pola mają grubość tylko w celu uzyskania ładnego obrazu. Problem dotyczy wymiarów wewnętrznych.
Kendall Frey

Odpowiedzi:

21

Naprzód

Poniżej znajduje się kula poza polem.

„Kula” to funkcja obliczania objętości f. Referencyjne przypadki testowe składają się na „pudełko”.

                     ( 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

Wynik:

0. 
0.523598775598299 
0.261799387799149 
0.279345334323962 
0.0654299441440212 
Darren Stone
źródło
5

Java - oparte na liczbach całkowitych

Ten program nie używa pi i nie wywołuje żadnej funkcji zewnętrznej - nawet sqrt. To tylko używa prostych działań arytmetycznych - +, -, *i /. Ponadto, oprócz kroku skalowania, działa wyłącznie z liczbami całkowitymi. Zasadniczo dzieli kulę na małe kostki i zlicza te, które są poza ramką.

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

Wynik:

0.0
0.5235867850933005
0.26178140856157484
0.27938608275528054
0.06542839088004015

W tej formie program wymaga więcej niż 2 GB pamięci (działa -Xmx2300mtutaj) i działa powoli. Wykorzystuje pamięć do wstępnego obliczenia liczby pierwiastków kwadratowych (arytmetycznie); nie jest to naprawdę konieczne, ale bez tego byłoby znacznie wolniej. Aby poprawić zarówno zapotrzebowanie na pamięć, jak i szybkość, zmniejsz wartość MINstałej (zmniejszy to jednak dokładność).

aditsu
źródło
2

Python 2 (podejście oparte na macierzy)

Tworzy tablicę tablic z wartościami prawdy, jeśli konkretny kwadrat w tej siatce znajduje się wewnątrz koła lub na zewnątrz koła. Powinno być bardziej precyzyjne, im większy okrąg rysujesz. Następnie wybiera obszar poniżej lub powyżej określonego rzędu i zlicza liczbę kwadratów należących do koła i dzieli je przez liczbę kwadratów w całym okręgu.

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
źródło
2

Python 2.7, Formuła sferycznej czapki

Ta wersja w niektórych przypadkach generuje ostrzeżenie o czasie wykonywania, ale nadal wyświetla poprawną odpowiedź.

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

Jeszcze dla 11 znaków mogę pozbyć się ostrzeżenia.

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

Oto przypadki testowe uruchomione w wersji 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
użytkownik2487951
źródło
Nawet jeśli nie jest to kod golf, można skrócić import numpy as ndo from numpy import*i zabrać wszystkie wymienione n.w kodzie.
Timtech,
@Timtech Dzięki za informacje i sugestie.
user2487951
1

Matematyka

Korzystanie z integracji numerycznej z odpowiednimi limitami.

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]]]}]
  ]
śmigać
źródło
0

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

Wynik:

0
0.523598775598299
0.261799387799149
0.279345334323962
0.0654299441440212
Kendall Frey
źródło
Nie rozumiem tych wyników. Pierwszy ma oczywiście wartość 0. Drugi nie ma wysokości, więc powinno być 1. Trzeci może pomieścić piłkę, a dokładnie połowa znajduje się nad nią (odpowiedź powinna wynosić 0,5). Pudełko w skrzynce 4 jest trochę za małe, więc spoczywa na pudełku. Odpowiedź powinna być nieco większa niż 0,5. Odpowiedź na ostatnią powinna wynosić> 0,5, ponieważ szerokość / długość nie jest wystarczająca, aby zmieścić piłkę w środku.
Sumurai8,
@ Sumurai8 „Dane wyjściowe: całkowita objętość ( nie procent ) kuli poza polem.”
Kendall Frey
0

Rubin

Zobaczmy ...
Jeśli pudełko jest całkowicie wewnątrz, to szerokość> średnica; długość> średnica i wysokość> średnica.
To powinien być pierwszy czek do uruchomienia.

Jeśli siedzi na dole, to w> d; l> dh h V=(pi*h^2 /3)*(3r-h)W takim przypadku, po prostu uzyskujemy wysokość i przez nią ją przejeżdżamy.

Jeśli utknie, używamy podobnej formuły ( V=(pi*h/6)*(3a^2 + h^2)). W rzeczywistości nasza wcześniejsza formuła jest oparta na tej! Zasadniczo używamy tego, a a jest po prostu mniejszym spośród w i l. (wskazówka, możemy uzyskać wysokość wykonując h=r-a)

Teraz kod!

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

Uwaga ** Nie testowałem tego zbyt wiele, więc mógł wkroczyć błąd, jeśli ktoś to zauważy, powiedz!
Matematyka jest jednak solidna.
Krótsza wersja:

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

(Teraz wiem na pewno, że pobieranie h dla v2 odbywa się inaczej, ale naprawię to później.


źródło
Ładny. Ten kod czyta się wyraźnie. Ale czy jesteś pewien co do następującego stwierdzenia? „możemy osiągnąć wysokość, robiąc to h=r-a” Właśnie czytałem o sferycznych formułach czapek , a schemat nie sugeruje związku tak prostego. Jeszcze raz przeczytam.
Darren Stone
@DarrenStone Teraz, kiedy patrzę wstecz, nie jestem pewien. Jestem wyjątkowo wyczerpany / wyczerpany, ale tak czy inaczej, bardzo łatwo jest go załatać!
Jestem prawie pewien, że a = wi > le ? le : wipowinien działać. W przeciwnym razie masz błąd.
Konrad Borowski
a = wi>le?le:winie działał. Zgaduję, że to dlatego, że korzystam z git ruby ​​(programista 2.2), mogło to oznaczać brak równowagi.
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;
}

Mój kod znajduje objętość bryły obrotu wykresu pewnej części półkola. pdbdutrzymuje liniową odległość rzutu punktu na powierzchni kuli, która styka się z krawędzią pudełka, do średnicy kuli, która, jeśli zostanie przedłużona, będzie normalna do dolnej części pudełka. Te dwa wyrażenia, które zawierają, M_PIsą w zasadzie anty pochodną całki pi * -(x^2)+2rxwzględem x (gdzie x jest miarą długości wzdłuż wspomnianej powyżej średnicy przez kulę, a gdzie r jest promieniem kuli) ocenianym dla jednego pdbdlub dwóch różnica średnicy kuli i głębokości skrzynki w zależności od konkretnego przypadku występującego przy różnych wymiarach.

użytkownik3142682
źródło