CJam, 28 27 octets
PP+mr_mc\ms]1.mrmqf*"(,)".\
Cette solution n'est pas basée sur le rejet. Je génère les points en coordonnées polaires, mais avec une distribution non uniforme des rayons pour obtenir une densité uniforme des points.
Testez-le ici.
Explication
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.
Pourquoi ça marche? Considérons un espace étroit de rayon r
et de (petite) largeurdr
. La zone est approximativement 2π*r*dr
(si l'espace annulaire est étroit, la circonférence intérieure et extérieure sont presque identiques, et la courbure peut être ignorée, de sorte que la zone peut être traitée comme celle d'un rectangle avec des longueurs latérales de la circonférence et la largeur de la anneau). La zone augmente donc linéairement avec le rayon. Cela signifie que nous voulons également une distribution linéaire des rayons aléatoires, afin d'atteindre une densité constante (à deux fois le rayon, il y a deux fois plus de surface à remplir, donc nous voulons deux fois plus de points là-bas).
Comment générer une distribution aléatoire linéaire de 0 à 1? Regardons d'abord le cas discret. Disons, nous avons une distribution souhaitée de 4 valeurs, comme {0.1, 0.4, 0.2, 0.3}
(c'est-à-dire que nous voulons 1
être 4 fois plus communs que 0
, et deux fois plus communs que 2
; nous voulons 3
trois fois plus communs que 0
):
Comment choisir l'une des quatre valeurs avec la distribution souhaitée? Nous pouvons les empiler, choisir une valeur uniformément aléatoire entre 0 et 1 sur l'axe des y et choisir le segment à ce point:
Il existe cependant une manière différente de visualiser cette cueillette. On pourrait plutôt remplacer chaque valeur de la distribution par l'accumulation des valeurs jusqu'à ce point:
Et maintenant, nous traitons la ligne supérieure de ce graphique comme une fonction f(x) = y
et l'inversons pour obtenir une fonction , que nous pouvons appliquer à une valeur uniformément aléatoire dans :g(y) = f-1(y) = x
y ∈ [0,1]
Cool, alors comment utiliser cela pour générer une distribution linéaire des rayons? Voici la distribution que nous voulons:
La première étape consiste à accumuler les valeurs de la distribution. Mais la distribution est continue, donc au lieu de résumer toutes les valeurs précédentes, nous prenons une intégrale de 0
à r
. On peut facilement résoudre ce analytiquement: . Cependant, nous voulons que cela soit normalisé, c'est-à-dire le multiplier par une constante telle que cela donne la valeur maximale de , donc ce que nous voulons vraiment, c'est :∫0r r dr = 1/2 r2
1
r
r2
Et enfin, nous inversons cela pour obtenir une fonction que nous pouvons appliquer à une valeur uniforme [0,1]
, que nous pouvons à nouveau faire analytiquement: c'est juste r = √y
, où y
est la valeur aléatoire:
Il s'agit d'une technique assez utile qui peut souvent être utilisée pour générer exactement des distributions simples (elle fonctionne pour n'importe quelle distribution, mais pour les plus compliquées, les deux dernières étapes peuvent devoir être résolues numériquement). Cependant, je ne l'utiliserais pas dans ce cas particulier dans le code de production, car la racine carrée, le sinus et le cosinus sont d'un coût prohibitif: l'utilisation d'un algorithme basé sur le rejet est en moyenne beaucoup plus rapide, car il n'a besoin que d'addition et de multiplication.