CJam, 28 27 byte
PP+mr_mc\ms]1.mrmqf*"(,)".\
Questa soluzione non è basata sul rifiuto. Sto generando i punti in coordinate polari, ma con una distribuzione non uniforme dei raggi per ottenere una densità uniforme dei punti.
Provalo qui.
Spiegazione
PP+ e# Push 2π.
mr_ e# Get a random float between 0 and 2π, make a copy.
mc\ e# Take the cosine of one copy and swap with the other.
ms] e# Take the sine of the other copy and wrap them in an array.
e# This gives us a uniform point on the unit circle.
1.mr e# Get a random float between 0 and 1.
mq e# Take the square root. This is the random radius.
f* e# Multiply x and y by this radius.
"(,)".\ e# Put the resulting numbers in the required format.
Perché funziona Considera uno stretto anello di raggio r
e (piccola) larghezza dr
. L'area è approssimativamente 2π*r*dr
(se l'anello è stretto, la circonferenza interna ed esterna sono quasi identiche e la curvatura può essere ignorata, in modo che l'area possa essere trattata come quella di un rettangolo con lunghezze laterali della circonferenza e larghezza del annulus). Quindi l'area aumenta linearmente con il raggio. Ciò significa che vogliamo anche una distribuzione lineare dei raggi casuali, al fine di ottenere una densità costante (al doppio del raggio, vi è il doppio dell'area da riempire, quindi vogliamo il doppio di punti lì).
Come possiamo generare una distribuzione casuale lineare da 0 a 1? Diamo prima un'occhiata al caso discreto. Diciamo, abbiamo una distribuzione desiderata di 4 valori, come {0.1, 0.4, 0.2, 0.3}
(cioè vogliamo 1
essere 4 volte più comuni di 0
, e due volte più comuni di 2
; vogliamo 3
tre volte più comuni di 0
):
Come può scegliere uno dei quattro valori con la distribuzione desiderata? Possiamo impilarli, scegliere un valore uniformemente casuale tra 0 e 1 sull'asse y e selezionare il segmento in quel punto:
Tuttavia, esiste un modo diverso di visualizzare questa selezione. Potremmo invece sostituire ogni valore della distribuzione con l'accumulo dei valori fino a quel punto:
E ora trattiamo la riga superiore di questo grafico come una funzione f(x) = y
e la invertiamo per ottenere una funzione , che possiamo applicare a un valore uniformemente casuale in :g(y) = f-1(y) = x
y ∈ [0,1]
Fantastico, quindi come può sfruttarlo per generare una distribuzione lineare dei raggi? Questa è la distribuzione che vogliamo:
Il primo passo è accumulare i valori della distribuzione. Ma la distribuzione è continua, quindi invece di sommare tutti i valori precedenti, prendiamo un integrale da 0
a r
. Siamo in grado di risolvere facilmente che analiticamente: . Tuttavia, vogliamo che questo sia normalizzato, cioè moltiplicandolo per una costante tale da dare il massimo valore , quindi quello che vogliamo davvero è :∫0r r dr = 1/2 r2
1
r
r2
E infine, invertiamo questo per ottenere una funzione in cui possiamo applicare un valore uniforme [0,1]
, che possiamo fare di nuovo analiticamente: è proprio r = √y
dove si y
trova il valore casuale:
Questa è una tecnica abbastanza utile che spesso può essere utilizzata per generare esattamente semplici distribuzioni (funziona per qualsiasi distribuzione, ma per quelle complicate potrebbe essere necessario risolvere numericamente gli ultimi due passaggi). Tuttavia, non lo userei in questo caso particolare nel codice di produzione, perché la radice quadrata, il seno e il coseno sono proibitivamente costosi: l'uso di un algoritmo basato sul rifiuto è in media molto più veloce, perché ha solo bisogno di addizioni e moltiplicazioni.