CJam, 28 27 bajtów
PP+mr_mc\ms]1.mrmqf*"(,)".\
To rozwiązanie nie opiera się na odrzuceniu. Generuję punkty we współrzędnych biegunowych, ale z nierównomiernym rozkładem promieni, aby uzyskać jednolitą gęstość punktów.
Sprawdź to tutaj.
Wyjaśnienie
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.
Dlaczego to działa? Rozważmy wąski pierścień o promieniu r
i (małej) szerokości dr
. Obszar jest w przybliżeniu 2π*r*dr
(jeśli pierścień jest wąski, obwód wewnętrzny i zewnętrzny są prawie identyczne, a krzywiznę można zignorować, tak że obszar można traktować jak prostokąt o bokach o obwodzie i szerokości pierścień). Zatem obszar zwiększa się liniowo wraz z promieniem. Oznacza to, że chcemy również liniowego rozkładu promieni losowych, aby uzyskać stałą gęstość (przy podwójnym promieniu jest dwa razy więcej miejsca do wypełnienia, więc chcemy tam dwa razy więcej punktów).
Jak wygenerować liniowy rozkład losowy od 0 do 1? Najpierw spójrzmy na dyskretny przypadek. Powiedzmy, że mamy pożądany rozkład 4 wartości, takich jak {0.1, 0.4, 0.2, 0.3}
(tzn. Chcemy 1
być 4 razy tak powszechne jak 0
i dwa razy tak powszechne jak 2
; chcemy 3
trzy razy częściej niż 0
):
Jak wybrać jedną z czterech wartości o pożądanym rozkładzie? Możemy je ułożyć w stos, wybrać równomiernie losową wartość od 0 do 1 na osi y i wybrać segment w tym punkcie:
Istnieje jednak inny sposób wizualizacji tego wyboru. Zamiast tego możemy zastąpić każdą wartość rozkładu akumulacją wartości do tego momentu:
A teraz traktujemy górną linię tego wykresu jako funkcję f(x) = y
i odwracamy ją, aby uzyskać funkcję , którą możemy zastosować do równomiernie losowej wartości w :g(y) = f-1(y) = x
y ∈ [0,1]
Fajnie, więc jak wykorzystać to do generowania liniowego rozkładu promieni? Oto rozkład, który chcemy:
Pierwszym krokiem jest zebranie wartości rozkładu. Ale rozkład jest ciągły, więc zamiast sumowania wszystkich poprzednich wartości, bierzemy całkę od 0
do r
. Możemy łatwo rozwiązać ten analitycznie: . Chcemy jednak to znormalizować, tzn. Pomnożyć przez stałą, tak aby to dało maksymalną wartość , więc tak naprawdę chcemy :∫0r r dr = 1/2 r2
1
r
r2
I na koniec odwracamy to, aby uzyskać funkcję, którą możemy zastosować do jednolitej wartości [0,1]
, którą możemy ponownie wykonać analitycznie: po prostu r = √y
, gdzie y
jest wartość losowa:
Jest to dość użyteczna technika, która często może być używana do generowania prostych rozkładów dokładnie (działa dla każdego rozkładu, ale w przypadku skomplikowanych dwa ostatnie kroki mogą wymagać rozwiązania numerycznego). Nie użyłbym go jednak w tym konkretnym przypadku w kodzie produkcyjnym, ponieważ pierwiastek kwadratowy, sinus i cosinus są zbyt drogie: użycie algorytmu opartego na odrzucaniu jest średnio znacznie szybsze, ponieważ wymaga tylko dodawania i mnożenia.