Natürlicher Pi # 1 - Sand


9

Tor

Generieren Sie ( N) zufällige Liniensegmente mit einheitlicher Länge ( l) und prüfen Sie, ob sie die äquidistanten ( t) parallelen Linien kreuzen .

Simulation

Was simulieren wir? Buffons Nadel . Glätten Sie den Sand in Ihrem Sandkasten und zeichnen Sie einen Satz gleichmäßig verteilter paralleler Linien (nennen Sie den Abstand dazwischen t). Nehmen Sie einen geraden Stock der Länge lund lassen Sie ihn Nmal in den Sandkasten fallen. Lassen Sie die Häufigkeit sein, mit der eine Linie überschritten wurde c. Dann Pi = (2 * l * n) / (t * c)!

Wie simulieren wir das?

  • Nehmen Sie Eingabe N,t,l
  • Mit N, t, lallen positiven ganzen Zahlen
  • Gehen Sie wie folgt vor N:
    • Generieren Sie eine gleichmäßig zufällige Ganzzahlkoordinate x,y
    • Mit 1 <= x, y <= 10^6
    • x,y ist der Mittelpunkt eines Liniensegments der Länge l
    • Generieren Sie eine einheitlich zufällige Ganzzahl a
    • Mit 1 <= a <= 180
    • Sei Pder Punkt, an dem das Liniensegment die x-Achse kreuzen würde
    • Dann aist der Winkel(x,y), P, (inf,0)
  • Zählen Sie die Anzahl cder Liniensegmente, die die Linie x = i*tfür eine beliebige Ganzzahl kreuzeni
  • Rückkehr (2 * l * N) / (t * c)

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Spezifikation

  • Eingang
    • Flexibel, Eingabe auf eine der Standardmethoden (z. B. Funktionsparameter, STDIN) und in einem beliebigen Standardformat (z. B. String, Binär)
  • Ausgabe
    • Flexibel, Ausgabe auf eine der Standardmethoden (z. B. Rückgabe, Druck)
    • Leerzeichen, nachgestellte und führende Leerzeichen sind akzeptabel
    • Genauigkeit, bitte geben Sie mindestens 4 Dezimalstellen Genauigkeit an (dh 3.1416)
  • Wertung
    • Der kürzeste Code gewinnt!

Testfälle

Ihre Ausgabe stimmt möglicherweise aufgrund zufälliger Zufälle nicht mit diesen überein. Aber im Durchschnitt sollten Sie ungefähr so ​​viel Genauigkeit für den gegebenen Wert von erhalten N, t, l.

Input (N,t,l)    ->  Output 
-----------        ------
10,10,5          -> ?.????
10,100,50        -> ?.????
1000,1000,600    -> 3.????
10000,1000,700   -> 3.1???
100000,1000,700  -> 3.14??

TL; DR

Diese Herausforderungen sind Simulationen von Algorithmen, die nur die Natur und Ihr Gehirn (und möglicherweise einige wiederverwendbare Ressourcen) benötigen, um Pi zu approximieren. Wenn Sie Pi während der Zombie-Apokalypse wirklich brauchen, verschwenden diese Methoden keine Munition ! Insgesamt gibt es neun Herausforderungen .


Ich dachte du hast schon Nummer 1 gemacht?
Conor O'Brien

1
@ ConorO'Brien Ich Null-Index es XD
NonlinearFruit

Das Problem dabei ist, dass Sie in Sprachen ohne komplexe Zahlen die Zahl 0..180 in 0..pi umwandeln müssen, was den Zweck des Nadelexperiments des Buffons eher zunichte macht.
Level River St

@NonlinearFruit Kann die Richtung aauch mit einer anderen Methode erstellt werden, wenn sie einheitlich ist? (Denken an eine 2D-Gauß-Blase)
Karl Napf

1
Kann man das annehmen t > l? Zwei der folgenden Lösungen gehen von dieser Annahme aus, die die Überprüfung auf Schnittpunkte erheblich vereinfacht.
Primo

Antworten:


9

R, 113 100 75 70 68 67 65 59 63 57 Bytes

Als statistische, funktionale Programmiersprache ist es nicht verwunderlich, dass R für diese Art von Aufgabe ziemlich gut geeignet ist. Die Tatsache, dass die meisten Funktionen vektorisierte Eingaben annehmen können, ist für dieses Problem sehr hilfreich, da Nwir nicht nur Iterationen durchlaufen, sondern nur Vektoren mit einer Größe übergeben N. Vielen Dank an @Billywob für einige Vorschläge, die dazu führen, dass 4 Bytes abgeschnitten werden. Vielen Dank an @Primo für die geduldige Erklärung, dass mein Code in Fällen t > l, in denen dies behoben wurde, nicht funktioniert hat .

pryr::f(2*l*N/t/sum(floor(runif(N)+sinpi(runif(N))*l/t)))

Probieren Sie es online aus!

Beispielausgabe:

N=1000, t=1000, l=500
3.037975

N=10000, t=1000, l=700
3.11943

N=100000, t=1000, l=700
3.140351

Erläuterung

Das Problem besteht darin, festzustellen, ob sich die beiden xWerte der Nadel auf beiden Seiten einer parallelen Linie befinden. Dies hat einige wichtige Konsequenzen:

  1. y-Werte sind irrelevant
  2. Die absolute Position auf der xAchse ist irrelevant, nur die Position relativ zu den nächsten parallelen Linien.

Im Wesentlichen ist dies eine Aufgabe in einem eindimensionalen Raum, in dem wir eine Linie mit der Länge in [0, l] erzeugen (der Winkel abestimmt diese Länge) und dann prüfen, wie oft diese Länge überschritten wird t. Der grobe Algorithmus lautet dann:

  1. Beispielwerte x1von [0, 1000000]. Da an jedem tPunkt entlang der xAchse parallele Linien auftreten , ist die relative xPosition xmodulo t.
  2. Probieren Sie einen Winkel aus a.
  3. Berechnen Sie die x2Position basierend auf a.
  4. Überprüfen Sie, wie oft hinein x1+x2passt t, dh das Wort ergreifen (x1+x2)/t.

Abtasten NZahlen in [0, 1E6] modulo tentspricht einfaches Abtasten NZahlen in [0, t]. Da dies (x1+x2)/täquivalent zu ist x1/t + x2/t, wird der erste Schritt zur Abtastung von [0, t] / t, dh [0, 1]. Glücklicherweise ist dies der Standardbereich für die runifFunktion von R , die Nreelle Zahlen von 0 bis 1 aus einer gleichmäßigen Verteilung zurückgibt .

                          runif(N)

Wir wiederholen diesen Schritt, um aden Winkel der Nadel zu erzeugen .

                                         runif(N)

Diese Zahlen werden als halbe Umdrehung interpretiert (dh .590 Grad). (Die OP fragen nach Grad von 1 bis 180, aber in den Kommentaren es ist klar , dass jedes Verfahren erlaubt, wenn sie als oder genauer ist.) Bei einem Winkel θ, sin(θ)gibt uns den x-Achsen - Abstand zwischen den Enden der Nadel. (Normalerweise würde man den Kosinus für so etwas wie diese verwendet, aber in unserem Fall betrachten wir den Winkel θin Bezug auf die y-Achse relativ zu sein, nicht die x-Achse (das heißt, ein Wert von 0 Grad geht nach oben , nicht rechts ), und deshalb verwenden wir den Sinus, der die Zahlen im Grunde phasenverschiebt.) Multipliziert ldamit ergibt sich die xPosition des Nadelendes.

                                   sinpi(runif(N))*l

Jetzt teilen wir durch tund addieren den x1Wert. Dies ergibt (x1+x2)/t, wie weit die Nadel x1in Bezug auf die Anzahl der parallelen Linien herausragt . Um die ganze Zahl zu erhalten, wie viele Linien gekreuzt wurden, nehmen wir die floor.

                    floor(runif(N)+sinpi(runif(N))*l/t)

Wir berechnen die Summe und geben an, cwie viele Linien von Nadeln gekreuzt werden.

                sum(floor(runif(N)+sinpi(runif(N))*l/t))

Der Rest des Codes implementiert nur die Formel zur Approximation von pi, d (2*l*N)/(t*c). H. Wir sparen einige Bytes in Klammern, indem wir die Tatsache ausnutzen, dass (2*l*N)/(t*c) == 2*l*N/t/c:

        2*l*N/t/sum(floor(runif(N)+sinpi(runif(N))*l/t))

Und das Ganze ist in eine anonyme Funktion verpackt:

pryr::f(2*l*N/t/sum(floor(runif(N)+sinpi(runif(N))*l/t)))

@rturnbull Schön! Sollten Sie die Klammern am Anfang nicht überspringen können? (2*l*N) => 2*l*N?
Billywob

@ Billywob Gut entdeckt! Vielen Dank.
Rturnbull

@rturnbull Oh und übrigens, (2*l*N)/(t*c) = 2*l*N/t/cSie können also zwei weitere Bytes speichern, indem Sie auch die Klammern im letzten Teil überspringen.
Billywob

@ Billywob Wieder gut entdeckt! Danke noch einmal.
Rturnbull

1
@primo Nochmals vielen Dank, es sollte jetzt behoben sein.
Rturnbull

6

Perl, 97 Bytes

#!perl -p
/ \d+/;$_*=2*$'/$&/map{($x=(1+~~rand 1e6)/$&)-$a..$x+($a=$'/$&/2*sin~~rand(180)*71/4068)-1}1..$_

Wenn man den Shebang als eins zählt, wird die Eingabe von stdin übernommen, wobei der Raum getrennt ist. Wenn nicht ganzzahlige Zufallswerte zulässig wären, könnte dies etwas kürzer sein.

Ich habe mir eine Freiheit genommen und π / 180 als 71/4068 angenähert , was innerhalb von 1,48 · 10 -9 genau ist .

Beispielnutzung

$ echo 1000000 1000 70000 | perl pi-sand.pl
3.14115345174061

Mehr oder weniger mathematisch äquivalente Substitutionen

Angenommen, die x-Koordinate repräsentiert den am weitesten links liegenden Punkt der Nadel und nicht ihre Mitte, wie in der Problembeschreibung angegeben:

89 Bytes

#!perl -p
/ \d+/;$_*=2*$'/$&/map{($x=(1+~~rand 1e6)/$&)..$x+($'/$&*sin~~rand(180)*71/4068)-1}1..$_

Das Problem gibt an, dass xals zufällige Ganzzahl abgetastet werden soll. Wenn wir den Zeilenabstand auf eine Lücke von eins projizieren, erhalten wir Werte der Form n/tmit 0 <= n < t, die nicht unbedingt einheitlich sind, wenn sie tsich nicht gleichmäßig teilen 1e6. Unter der Annahme, dass eine gleichmäßige Verteilung dennoch akzeptabel ist:

76 Bytes

#!perl -p
/ \d+/;$_*=2*$'/$&/map{($x=rand)..$x+($'/$&*sin~~rand(180)*71/4068)-1}1..$_

Beachten Sie, dass randes am Anfang des Bereichs nicht erforderlich ist , da immer kleiner als eins ist (und daher auf Null abgeschnitten wird):

70 Bytes

#!perl -p
/ \d+/;$_*=2*$'/$&/map{1..(rand)+($'/$&*sin~~rand(180)*71/4068)}1..$_

Angenommen, der Winkel der Nadel muss kein ganzzahliger Grad sein, sondern nur gleichmäßig zufällig:

59 Bytes

#!perl -p
/ \d+/;$_*=2*$'/$&/map{1..(rand)+abs$'/$&*sin rand$`}1..$_

Angenommen, der Winkel kann eine gleichmäßige Verteilung sein:

52 Bytes

#!perl -p
/ \d+/;$_*=2*$'/$&/map{1..(rand)+abs$'/$&*sin}1..$_

Das Obige ist eine mathematisch korrekte Simulation von Buffons Nadel. An diesem Punkt denke ich jedoch, dass die meisten Leute zustimmen würden, dass dies nicht das ist, wonach die Frage gestellt wurde.


Wirklich pushen

Wir könnten einfach die Hälfte der Testfälle wegwerfen, wenn der zweite Endpunkt links vom ersten liegt (anstatt sie auszutauschen):

47 Bytes

#!perl -p
/ \d+/;$_*=$'/$&/map{1..(rand)+$'/$&*sin}1..$_

Beachten Sie, dass die Werte von tund lfür die Ergebnisse des Experiments keine Rolle spielen. Wir könnten sie einfach ignorieren (implizit davon ausgehen, dass sie gleich sind):

28 Bytes

#!perl -p
$_/=map{1..(rand)+sin}1..$_

Offensichtlich nicht konkurrierend, aber Sie müssen zugeben, dass es eine gewisse Eleganz hat.


4

Python 2, 141 Bytes

schamloser Hafen von rtumbull, der schon überspringt, yweil er überhaupt nicht benötigt wird.

from math import*
from random import*
lambda N,t,l:(2.*l*N)/(t*sum(randint(1,1e6)%t+abs(cos(randint(1,180)*pi/180))*l>t for _ in range(N)))

Das Problem ist nur, dass pi bereits im Programm bekannt ist.

Hier ist es (golfbar) mit unbekanntem pi und ohne trigonometrische Funktionen

def g(N,t,l):
 c=0
 for _ in range(N):
    x,y=gauss(0,1),gauss(0,1);c+=randint(1,1e6)%t+abs(x/sqrt(x*x+y*y))*l>t
 return(2.*l*N)/(t*c)

x,yin gist nur für die Richtung.


Benötigt from random import randint;from math import cos,pi. Schlägt t < lz 1000000,1000,70000.
Primo
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.