Natural Pi # 1 - Sand


9

Obbiettivo

Genera ( N) segmenti di linea casuali di lunghezza uniforme ( l), controlla se attraversano le tlinee parallele equidistanti ( ).

Simulazione

Cosa stiamo simulando? Ago di Buffon . Leviga la sabbia nella tua sandbox, disegna una serie di linee parallele equidistanti (chiama la distanza in mezzo t). Prendi un bastoncino dritto di lunghezza le lascialo cadere Nnella sandbox. Lascia che sia il numero di volte che ha attraversato una linea c. Allora Pi = (2 * l * n) / (t * c)!

Come stiamo simulando questo?

  • Prendi input N,t,l
  • Con N, t, ltutti i numeri interi positivi
  • Fai i seguenti Norari:
    • Genera una coordinata intera uniformemente casuale x,y
    • Con 1 <= x, y <= 10^6
    • x,y è il centro di un segmento di linea di lunghezza l
    • Genera un numero intero uniformemente casuale a
    • Con 1 <= a <= 180
    • Lascia che Psia il punto in cui il segmento di linea attraverserebbe l'asse x
    • Quindi aè l'angolo(x,y), P, (inf,0)
  • Contare il numero cdi segmenti di linea che attraversano la linea x = i*tper qualsiasi numero interoi
  • Ritorno (2 * l * N) / (t * c)

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

specificazione

  • Ingresso
    • Flessibile, accetta input in uno dei modi standard (es. Parametro di funzione, STDIN) e in qualsiasi formato standard (es. String, Binary)
  • Produzione
    • Flessibile, fornisce output in uno dei modi standard (ad es. Ritorno, stampa)
    • Sono ammessi spazi bianchi, finali e spazi bianchi iniziali
    • Precisione, si prega di fornire almeno 4 cifre decimali di precisione (es. 3.1416)
  • punteggio
    • Vince il codice più corto!

Casi test

L'output potrebbe non allinearsi con questi, a causa di possibilità casuali. Ma in media, dovresti ottenere tanta precisione per il valore dato di 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

Queste sfide sono simulazioni di algoritmi che richiedono solo la natura e il cervello (e forse alcune risorse riutilizzabili) per approssimare Pi. Se hai davvero bisogno di Pi durante l'apocalisse di zombi, questi metodi non sprecano munizioni ! Ci sono nove sfide in totale.


Pensavo avessi già fatto il numero 1?
Conor O'Brien,

1
@ ConorO'Brien I zero-index it XD
NonlinearFruit

il problema è che, nelle lingue senza numeri complessi, è necessario trasformare il numero 0..180 in 0..pi, che piuttosto vanifica lo scopo dell'esperimento dell'ago del buffone.
Level River St

@NonlinearFruit la direzione può aessere creata anche con un altro metodo, se è uniforme? (pensando a una bolla Gauss 2D)
Karl Napf,

1
Si può presumere che t > l? Due soluzioni di seguito fanno questo presupposto, che semplifica un po 'il controllo dell'intersezione.
primo

Risposte:


9

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

Come linguaggio di programmazione statistico e funzionale, non sorprende che R sia abbastanza adatto a questo tipo di attività. Il fatto che la maggior parte delle funzioni possa accettare input vettorializzati è davvero utile per questo problema, in quanto invece di passare in rassegna le Niterazioni, passiamo solo intorno a vettori di dimensioni N. Grazie a @Billywob per alcuni suggerimenti che portano a tagliare 4 byte. Mille grazie a @Primo per avermi spiegato pazientemente come il mio codice non funzionava nei casi in cui t > l, che ora è stato corretto.

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

Provalo online!

Uscita campione:

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

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

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

Spiegazione

Il problema si riduce a determinare se i due xvalori dell'ago si trovano su entrambi i lati di una linea parallela. Ciò ha alcune conseguenze importanti:

  1. yi valori sono irrilevanti
  2. La posizione assoluta xsull'asse è irrilevante, solo la posizione relativa alle linee parallele più vicine.

Essenzialmente si tratta di un'attività in uno spazio monodimensionale, in cui generiamo una linea con lunghezza in [0, l] (l'angolo adetermina questa lunghezza), quindi controlliamo per vedere quante volte questa lunghezza supera t. L'algoritmo grezzo è quindi:

  1. x1Valori di esempio da [0, 1000000]. Poiché le linee parallele si verificano in ogni tpunto lungo l' xasse, la relativa xposizione è xmodulo t.
  2. Campiona un angolo a.
  3. Calcola la x2posizione in base a a.
  4. Controlla quante volte si x1+x2inserisce t, ovvero prendi la parola (x1+x2)/t.

I Nnumeri di campionamento in [0, 1e6] modulo tequivalgono al semplice campionamento di Nnumeri in [0, t]. Poiché (x1+x2)/tè equivalente a x1/t + x2/t, il primo passo diventa il campionamento da [0, t] / t, ovvero [0, 1]. Fortunatamente per noi, questo è l'intervallo predefinito per la runiffunzione di R , che restituisce Nnumeri reali da 0 a 1 da una distribuzione uniforme.

                          runif(N)

Ripetiamo questo passaggio per generare al'angolo dell'ago.

                                         runif(N)

Questi numeri sono interpretati come mezzo giro (ovvero .590 gradi). (L'OP richiede gradi da 1 a 180, ma nei commenti viene chiarito che è consentito qualsiasi metodo se è come o più preciso.) Per un angolo θ, sin(θ)ci fornisce la distanza dell'asse x tra le estremità dell'ago. (Normalmente di usano il coseno di qualcosa di simile, ma nel nostro caso, stiamo considerando l'angolo θcome relativi al l'asse y, non l'asse x (cioè un valore di 0 gradi va alto , non a destra ), e quindi usiamo il seno, che sostanzialmente sposta i numeri di fase.) Moltiplicato per lquesto ci dà la xposizione dell'estremità dell'ago.

                                   sinpi(runif(N))*l

Ora dividiamo per te aggiungiamo il x1valore. Questo produce (x1+x2)/t, che è la distanza da cui sporge l'ago x1, in termini di numero di linee parallele. Per ottenere il numero intero di quante linee sono state incrociate, prendiamo il floor.

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

Calcoliamo la somma, dandoci il conteggio cdi quante linee sono attraversate da aghi.

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

Il resto del codice sta semplicemente implementando la formula per approssimare pi, cioè (2*l*N)/(t*c). Salviamo alcuni byte tra parentesi sfruttando il fatto che (2*l*N)/(t*c) == 2*l*N/t/c:

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

E il tutto è avvolto in una funzione anonima:

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

@rturnbull Nice one! Tuttavia, non dovresti riuscire a saltare le parentesi all'inizio? (2*l*N) => 2*l*N?
Billywob,

@Billywob Ben notato! Grazie.
rturnbull,

@rturnbull Oh e comunque, (2*l*N)/(t*c) = 2*l*N/t/ccosì puoi salvare altri due byte saltando anche le parentesi sull'ultima parte.
Billywob,

@Billywob Ancora una volta, ben individuato! Grazie ancora.
rturnbull,

1
@primo Grazie ancora, dovrebbe essere riparato ora.
rturnbull,

6

Perl, 97 byte

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

Contando lo shebang come uno, l'input è preso dallo stdin, lo spazio è separato. Se fossero consentiti valori casuali non interi, questo potrebbe essere leggermente più breve.

Ho preso una libertà, approssimando π / 180 a 71/4068 , che è preciso entro 1,48 · 10-9 .

Esempio di utilizzo

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

Sostituzioni più o meno matematicamente equivalenti

Supponendo che la coordinata x rappresenti il ​​punto più a sinistra dell'ago, piuttosto che il suo centro, come specificato nella descrizione del problema:

89 byte

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

Il problema specifica che xdeve essere campionato come un numero intero casuale. Se proiettiamo la spaziatura della linea su uno spazio di uno, questo ci lascerà con valori della forma n/tcon 0 <= n < t, non necessariamente uniforme, se tnon si divide uniformemente 1e6. Supponendo che una distribuzione uniforme sia comunque accettabile:

76 byte

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

Si noti che poiché randsarà sempre inferiore a uno (e quindi troncato a zero), non è necessario all'inizio dell'intervallo:

70 byte

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

Supponendo che l'angolo dell'ago non debba essere un numero intero, ma solo uniformemente casuale:

59 byte

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

Supponendo che l'angolo possa essere qualsiasi distribuzione uniforme:

52 byte

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

Quanto sopra è una simulazione matematicamente corretta dell'ago di Buffon. Tuttavia, a questo punto penso che la maggior parte delle persone concorderebbe sul fatto che questo non è in realtà ciò che la domanda ha posto.


Lo spingo davvero

Potremmo semplicemente eliminare la metà dei casi di test, ogni volta che il secondo endpoint si trova a sinistra del primo (anziché sostituirli):

47 byte

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

Tieni presente che i valori di te non lsono pertinenti ai risultati dell'esperimento. Potremmo semplicemente ignorarli (assumendoli implicitamente come uguali):

28 byte

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

Ovviamente non competitivo, ma devi ammetterlo, ha una certa eleganza.


4

Python 2, 141 byte

porto spudorato di rtumbull, già saltando yperché totalmente non necessario.

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

Il problema è solo che pi è già noto nel programma.

Eccolo (golfabile) con pi sconosciuto e nessuna funzione trigonometrica

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 gè solo per la direzione.


Richiede from random import randint;from math import cos,pi. Non funziona t < l, ad es 1000000,1000,70000.
primo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.