CJam, 28 27 bytes
PP+mr_mc\ms]1.mrmqf*"(,)".\
Esta solução não é baseada em rejeição. Estou gerando os pontos em coordenadas polares, mas com uma distribuição não uniforme dos raios para obter uma densidade uniforme dos pontos.
Teste aqui.
Explicação
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.
Por que isso funciona? Considere um anel estreito de raio r
e largura (pequena) dr
. A área é aproximadamente 2π*r*dr
(se o anel for estreito, a circunferência interna e externa são quase idênticas e a curvatura pode ser ignorada, de modo que a área possa ser tratada como a de um retângulo com comprimentos laterais da circunferência e a largura da anel). Portanto, a área aumenta linearmente com o raio. Isso significa que também queremos uma distribuição linear dos raios aleatórios, a fim de obter uma densidade constante (com o dobro do raio, há o dobro da área a ser preenchida, portanto, queremos o dobro do número de pontos).
Como geramos uma distribuição aleatória linear de 0 a 1? Vejamos o caso discreto primeiro. Digamos, temos uma distribuição desejada de 4 valores, como {0.1, 0.4, 0.2, 0.3}
(ou seja, queremos 1
ser 4 vezes mais comuns que 0
e duas vezes mais comuns que 2
; queremos 3
três vezes mais comuns que 0
):
Como escolher um dos quatro valores com a distribuição desejada? Podemos empilhá-los, escolher um valor aleatoriamente uniforme entre 0 e 1 no eixo y e escolher o segmento nesse ponto:
Existe uma maneira diferente de visualizar essa seleção. Em vez disso, poderíamos substituir cada valor da distribuição pela acumulação dos valores até esse ponto:
E agora tratamos a linha superior deste gráfico como uma função f(x) = y
e a invertemos para obter uma função , que podemos aplicar a um valor aleatoriamente uniforme em :g(y) = f-1(y) = x
y ∈ [0,1]
Legal, então como usar isso para gerar uma distribuição linear de raios? Esta é a distribuição que queremos:
O primeiro passo é acumular os valores da distribuição. Mas a distribuição é contínua, portanto, em vez de somar todos os valores anteriores, usamos uma integral de 0
para r
. Podemos facilmente resolver esse analiticamente: . No entanto, queremos que isso seja normalizado, ou seja, multiplicá-lo por uma constante de modo que isso dê o valor máximo de , então o que realmente queremos é :∫0r r dr = 1/2 r2
1
r
r2
E, finalmente, invertemos isso para obter uma função na qual podemos aplicar um valor uniforme [0,1]
, o que podemos fazer novamente analiticamente: é apenas r = √y
, onde y
está o valor aleatório:
Essa é uma técnica bastante útil que geralmente pode ser usada para gerar exatamente distribuições simples (funciona para qualquer distribuição, mas para complicadas, as duas últimas etapas podem ter que ser resolvidas numericamente). No entanto, eu não o usaria neste caso específico no código de produção, porque a raiz quadrada, o seno e o cosseno são proibitivamente caros: o uso de um algoritmo baseado em rejeição é, em média, muito mais rápido, porque só precisa de adição e multiplicação.