Gere um ponto aleatório dentro de um círculo (uniformemente)


212

Preciso para gerar um ponto uniformemente aleatório dentro de um círculo de raio R .

Percebo que, apenas escolhendo um ângulo aleatoriamente uniforme no intervalo [0 ... 2π) e raio aleatoriamente uniforme no intervalo (0 ... R ) eu terminaria com mais pontos em direção ao centro, pois para dois dados raios, os pontos no raio menor ficarão mais próximos um do outro do que nos pontos no raio maior.

Encontrei uma entrada de blog sobre isso aqui, mas não entendo o raciocínio dele. Acho que é correto, mas eu realmente gostaria de entender de onde ele recebe (2 / R 2 ) × r e como ele deriva a solução final.


Atualização: 7 anos após postar esta pergunta, eu ainda não recebi uma resposta satisfatória sobre a questão real sobre a matemática por trás do algoritmo de raiz quadrada. Então, eu passei um dia escrevendo uma resposta. Link para a minha resposta .


18
A desvantagem da amostragem por rejeição é realmente um grande problema? O número esperado de tentativas necessárias é 4 / π 1,27, e a probabilidade de você precisar de mais do que k tentativas é (1-π / 4) ^ k. Para k = 20 , este é ≈ .00000000000004 e para k = 50 é da ordem de 10 ^ {- 34}. Você pode aproveitar essas chances a qualquer dia; você vai ficar bem.
ShreevatsaR

3
Na verdade, a amostragem por rejeição fornece uma garantia para a rescisão. As chances são infinitamente baixas (para ser preciso, zero) de que seu algoritmo nunca termine.
Jared Nielsen

2
Na minha opinião, a importância da desvantagem da amostragem por rejeição é proporcional à facilidade de usar um método de amostragem que evite a rejeição. Nesse caso, a desvantagem é importante porque a amostragem sem rejeição é simples.
spex 27/08/14

4
@spex Na prática, a técnica de rejeição é mais rápida, pois evita a necessidade de avaliações da função transcendental.
pjs

2
(cont) rejeição: 0,52s Todos deram médias e desvios padrão idênticos (a 3 sig. fig). Como esperado, a amostragem de rejeição falhou 27% do tempo (4 / pi-1) e, portanto, precisou 27% mais números aleatórios do que btilly, mas 15% menos que o sigfpe. Isso confirma os comentários feitos por pjs e outros que a amostragem por rejeição é provavelmente a melhor abordagem, a menos que os randoms sejam muito caros para gerar.
Peter Davidson

Respostas:


189

Vamos abordar isso como Arquimedes faria.

Como podemos gerar um ponto uniformemente em um triângulo ABC, onde | AB | = | BC |? Vamos facilitar isso estendendo para um paralelogramo ABCD. É fácil gerar pontos uniformemente no ABCD. Escolhemos uniformemente um ponto aleatório X em AB e Y em BC e escolhemos Z de modo que XBYZ seja um paralelogramo. Para obter um ponto uniformemente escolhido no triângulo original, basta dobrar os pontos que aparecem no ADC de volta ao ABC ao longo de AC.

Agora considere um círculo. No limite, podemos pensar nele como infinitos triângulos de isoceles ABC, com B na origem e A e C na circunferência que desaparecem um do outro. Podemos escolher um desses triângulos simplesmente escolhendo um ângulo teta. Portanto, agora precisamos gerar uma distância do centro escolhendo um ponto na tira ABC. Novamente, estenda para ABCD, onde D agora é duas vezes o raio do centro do círculo.

Escolher um ponto aleatório no ABCD é fácil usando o método acima. Escolha um ponto aleatório em AB. Escolha uniformemente um ponto aleatório em BC. Ou seja. escolha um par de números aleatórios x e y uniformemente em [0, R], dando distâncias do centro. Nosso triângulo é uma tira fina, portanto AB e BC são essencialmente paralelos. Portanto, o ponto Z é simplesmente uma distância x + y da origem. Se x + y> R, dobramos novamente.

Aqui está o algoritmo completo para R = 1. Espero que você concorde que é bem simples. Ele usa trigonometria, mas você pode garantir quanto tempo levará e quantas random()chamadas serão necessárias, diferentemente da amostragem por rejeição.

t = 2*pi*random()
u = random()+random()
r = if u>1 then 2-u else u
[r*cos(t), r*sin(t)]

Aqui está no Mathematica.

f[] := Block[{u, t, r},
  u = Random[] + Random[];
  t = Random[] 2 Pi;
  r = If[u > 1, 2 - u, u];
  {r Cos[t], r Sin[t]}
]

ListPlot[Table[f[], {10000}], AspectRatio -> Automatic]

insira a descrição da imagem aqui


6
@Karelzarath Gosto da noção contra-intuitiva de um triângulo infinitamente fino que ainda é mais largo em uma extremidade do que na outra :-) É a resposta certa.
SIGFPE

2
@hammar Não tenho certeza se generaliza bem para n dimensões. Mas para 3d você pode usar outro resultado de Archimedes! Use o teorema da "caixa de chapéu" para gerar um ponto no cilindro (fácil!) E depois mapeie-o de volta para a esfera. Isso dá uma direção. Agora use random()+random()+random()com uma dobra mais complexa (ou seja, uma dobra de 6 direções de um paralelepípedo infinitesimalmente fino com um terahedro). Não estou convencido de que este seja um bom método.
Signfpe

2
Pensei 1 min para descobrir diferença entre random () + random () e 2 * random () ... Eu sou tão estúpido: /
JiminP

3
@Tharwen Observe como em um círculo há mais pontos no raio 0.9-1.0 do que no raio 0.0-0.1. random () + random () gera raios com maior probabilidade de ficar em torno de 1,0, mas estão no intervalo de 0,0-2,0. Quando dobradas, é mais provável que estejam em torno de 1,0 e sempre na faixa de 0,0-1,0. Além disso, é exatamente a proporção necessária na primeira frase deste comentário. Apenas reduzir para metade produz mais números em torno da marca 0,5 e isso seria errado.
21412 signfpe

2
@Tharwen Tente usar os dois esquemas para gerar números aleatórios e ver o que você recebe. 2 * random () fornece números uniformemente distribuídos no intervalo de 0 a 2. random () + random () fornece números no intervalo de 0 a 2, mas (geralmente) haverá mais números perto de 1,0 do que próximo de 0,0 ou 2,0. É como jogar dois dados e somar mais provavelmente dá 7 do que qualquer outro número.
sigfpe 22/10/12

133

Como gerar um ponto aleatório dentro de um círculo de raio R :

r = R * sqrt(random())
theta = random() * 2 * PI

(Assumindo que random()fornece um valor entre 0 e 1 uniformemente)

Se você deseja converter isso em coordenadas cartesianas, pode fazer

x = centerX + r * cos(theta)
y = centerY + r * sin(theta)


Por que sqrt(random())?

Vamos olhar para a matemática que leva até sqrt(random()). Suponha, por simplicidade, que estamos trabalhando com o círculo unitário, ou seja, R = 1.

A distância média entre os pontos deve ser a mesma, independentemente da distância do centro que olhamos. Isso significa, por exemplo, que, olhando o perímetro de um círculo com circunferência 2, deveríamos encontrar o dobro de pontos que o número de pontos no perímetro de um círculo com circunferência 1.


                

Como a circunferência de um círculo (2π r ) cresce linearmente com r , segue-se que o número de pontos aleatórios deve crescer linearmente com r . Em outras palavras, a função de densidade de probabilidade desejada (PDF) cresce linearmente. Como um PDF deve ter uma área igual a 1 e o raio máximo é 1, temos


                

Portanto, sabemos como deve ser a densidade desejada de nossos valores aleatórios. Agora: como geramos um valor tão aleatório quando tudo o que temos é um valor aleatório uniforme entre 0 e 1?

Usamos um truque chamado amostragem por transformação inversa

  1. No PDF, crie a função de distribuição cumulativa (CDF)
  2. Espelhe isso ao longo de y = x
  3. Aplique a função resultante a um valor uniforme entre 0 e 1.

Parece complicado? Deixe-me inserir uma citação em bloco com uma pequena faixa lateral que transmite a intuição:

Suponha que desejamos gerar um ponto aleatório com a seguinte distribuição:

                

Isso é

  • 1/5 dos pontos uniformemente entre 1 e 2, e
  • 4/5 dos pontos uniformemente entre 2 e 3.

O CDF é, como o nome sugere, a versão cumulativa do PDF. Intuitivamente: Enquanto o PDF ( x ) descreve o número de valores aleatórios em x , o CDF ( x ) descreve o número de valores aleatórios menores que x .

Nesse caso, o CDF seria semelhante a:

                

Para ver como isso é útil, imagine que atiramos em balas da esquerda para a direita em alturas uniformemente distribuídas. Quando as balas atingem a linha, caem no chão:

                

Veja como a densidade das balas no chão corresponde à nossa distribuição desejada! Estamos quase lá!

O problema é que, para esta função, o eixo y é a saída e o eixo x é a entrada . Só podemos "disparar balas do chão para cima"! Precisamos da função inversa!

É por isso que espelhamos a coisa toda; x se torna y e Y torna-se x :

                

Chamamos isso de CDF -1 . Para obter valores de acordo com a distribuição desejada, usamos CDF -1 (random ()).

… Então, voltemos a gerar valores aleatórios de raio em que nosso PDF é igual a 2 x .

Etapa 1: criar o CDF:

como trabalhamos com reais, o CDF é expresso como a integral do PDF.

CDF ( x ) = x 2 x = x 2

Etapa 2: espelhe o CDF ao longo de y = x :

Matematicamente isso se resume a trocar x e y e resolvendo para y :

CDF :      y = x 2
Troca:    x = y 2
Resolva:    y = √ x
CDF -1 :   y = √ x

Etapa 3: aplique a função resultante a um valor uniforme entre 0 e 1

CDF -1 (aleatório ()) = aleatório ()

Qual é o que nos propusemos a derivar :-)


Esse algoritmo pode ser usado para gerar pontos no anel com eficiência.
Ivan Kovtun

No ringue? Como com um raio fixo? Não tenho certeza se entendi sua pergunta, mas se você tem um raio fixo, basta aleatorizar o ângulo.
aioobe 31/07/19

2
Tentei usar a palavra mais simples "Anel" em vez da região de Annulus, limitada por dois círculos concêntricos. Nesse caso, o algoritmo de rejeição não se torna eficaz e o primeiro algoritmo superior é difícil de generalizar. E a caixa de canto com um raio também é coberta com seu algoritmo. Sempre geramos raio como sqrt (aleatório (min_radius ^ 2, max_radius ^ 2)) mesmo quando min_radius == max_radius.
Ivan Kovtun

1
Ah legal! Para ser claro, quando você diz random(min_radius², max_radius²), você quer dizer algo equivalente a random() * (max_radius² - min_radius²) + min_radius², where random()retorna um valor uniforme entre 0 e 1?
Aioobe 31/07/19

sim, é exatamente isso que eu quero dizer: raio = sqrt (aleatório () * (max_radius² - min_radius²) + min_radius²).
Ivan Kovtun

27

Aqui está uma solução rápida e simples.

Escolha dois números aleatórios no intervalo (0, 1), ou seja, ae b. Se b < a, troque-os. O seu ponto é (b*R*cos(2*pi*a/b), b*R*sin(2*pi*a/b)).

Você pode pensar sobre esta solução da seguinte maneira. Se você pegar o círculo, cortá-lo e endireitá-lo, obterá um triângulo retângulo. Escala que para baixo triângulo, e você teria um triângulo a partir (0, 0)de (1, 0)para (1, 1)e de volta para (0, 0). Todas essas transformações alteram a densidade uniformemente. O que você fez foi escolher uniformemente um ponto aleatório no triângulo e reverter o processo para obter um ponto no círculo.


Este, por alguma razão, me dá distribuição muito mais uniforme do que a resposta aceita, embora eu tinha necessidade de dividir a coordenada pelo raio, caso contrário é dentro de um círculo de R ^ 2
Greg Zaal

3
Obrigado, este é o seu código em Java, talvez alguém o considere útil: float random1 = MathUtils.random (); flutuar random2 = MathUtils.random (); float randomXPoint = random2 * raio MathUtils.cos (MathUtils.PI2 * random1 / random2); float randomYPoint = random2 * raio MathUtils.sin (MathUtils.PI2 * random1 / random2);
Tony Ceralva

muito bom! Eu gosto da ideia de mais probabilidade de centralizar os pontos, por isso, se não trocarmos quando b < apodemos conseguir isso! por exemplo, em javascript jsfiddle.net/b0sb5ogL/1
Guilherme

Eu acho que sua solução é ruim. Não está dando resultados uniformes. Verifique esta captura de tela prntscr.com/fizxgc
bolec_kolec

4
Você pode explicar um pouco mais como cortar o círculo e endireitá-lo?
KEC

21

Note-se a densidade de pontos na proporcional ao inverso do quadrado do raio, portanto, em vez de escolher ra partir [0, r_max], escolher [0, r_max^2], em seguida, calcular suas coordenadas como:

x = sqrt(r) * cos(angle)
y = sqrt(r) * sin(angle)

Isso fornecerá uma distribuição uniforme de pontos em um disco.

http://mathworld.wolfram.com/DiskPointPicking.html


12

Pense nisso desta maneira. Se você possui um retângulo em que um eixo é raio e outro é ângulo, e você pega os pontos dentro desse retângulo que estão próximos do raio 0. Todos eles ficarão muito próximos da origem (que estão próximos no círculo). os pontos próximos ao raio R, todos cairão perto da borda do círculo (ou seja, afastados um do outro).

Isso pode lhe dar uma idéia de por que você está recebendo esse comportamento.

O fator derivado nesse link informa a quantidade de área correspondente no retângulo que precisa ser ajustada para não depender do raio depois que ele é mapeado para o círculo.

Edit: Então, o que ele escreve no link que você compartilha é: "Isso é fácil o suficiente, calculando o inverso da distribuição cumulativa, e obtemos o r:".

A premissa básica é aqui que você pode criar uma variável com uma distribuição desejada a partir de um uniforme, mapeando o uniforme pela função inversa da função de distribuição cumulativa da função de densidade de probabilidade desejada. Por quê? Apenas tome como garantido por enquanto, mas isso é um fato.

Aqui está a minha explicação intuitiva da matemática. A função de densidade f (r) em relação a r deve ser proporcional a r em si. Compreender esse fato faz parte de qualquer livro de cálculo básico. Veja as seções sobre os elementos da área polar. Alguns outros pôsteres mencionaram isso.

Então, vamos chamá-lo de f (r) = C * r;

Isso acaba sendo a maior parte do trabalho. Agora, como f (r) deve ser uma densidade de probabilidade, você pode facilmente ver que, ao integrar f (r) no intervalo (0, R), obtém-se que C = 2 / R ^ 2 (este é um exercício para o leitor .)

Assim, f (r) = 2 * r / R ^ 2

OK, é assim que você obtém a fórmula no link.

Então, a parte final vai da variável aleatória uniforme u em (0,1), você deve mapear pela função inversa da função de distribuição cumulativa a partir dessa densidade desejada f (r). Para entender por que esse é o caso, é necessário encontrar um texto de probabilidade avançado como Papoulis (ou derivá-lo você mesmo).

Integrando f (r) você obtém F (r) = r ^ 2 / R ^ 2

Para encontrar a função inversa disso, defina u = r ^ 2 / R ^ 2 e depois resolva para r, que fornece r = R * sqrt (u)

Isso também faz sentido de maneira intuitiva, u = 0 deve mapear para r = 0. Além disso, u = 1 shoudl mapeia para r = R. Além disso, ele segue a função de raiz quadrada, que faz sentido e corresponde ao link.


10

A razão pela qual a solução ingênua não funciona é que ela fornece uma densidade de probabilidade mais alta para os pontos mais próximos do centro do círculo. Em outras palavras, o círculo que possui o raio r / 2 tem probabilidade r / 2 de obter um ponto selecionado, mas possui a área (número de pontos) pi * r ^ 2/4.

Portanto, queremos que a densidade de probabilidade do raio tenha a seguinte propriedade:

A probabilidade de escolher um raio menor ou igual a um dado r deve ser proporcional à área do círculo com raio r. (porque queremos ter uma distribuição uniforme nos pontos e áreas maiores significam mais pontos)

Em outras palavras, queremos que a probabilidade de escolher um raio entre [0, r] seja igual à sua parcela da área geral do círculo. A área total do círculo é pi * R ^ 2, e a área do círculo com raio r é pi * r ^ 2. Assim, gostaríamos que a probabilidade de escolher um raio entre [0, r] seja (pi * r ^ 2) / (pi * R ^ 2) = r ^ 2 / R ^ 2.

Agora vem a matemática:

A probabilidade de escolher um raio entre [0, r] é a integral de p (r) dr de 0 a r (isso é apenas porque adicionamos todas as probabilidades dos raios menores). Assim, queremos integral (p (r) dr) = r ^ 2 / R ^ 2. Podemos ver claramente que R ^ 2 é uma constante, então tudo o que precisamos fazer é descobrir qual p (r), quando integrado, nos daria algo como r ^ 2. A resposta é claramente r * constante. integral (r * constante dr) = r ^ 2/2 * constante. Isso deve ser igual a r ^ 2 / R ^ 2, portanto constante = 2 / R ^ 2. Assim, você tem a distribuição de probabilidade p (r) = r * 2 / R ^ 2

Nota: Outra maneira mais intuitiva de pensar sobre o problema é imaginar que você está tentando dar a cada círculo de raio uma densidade de probabilidade igual à proporção do número de pontos que ele tem em sua circunferência. Assim, um círculo com raio r terá 2 * pi * r "pontos" em sua circunferência. O número total de pontos é pi * R ^ 2. Portanto, você deve dar ao círculo ra uma probabilidade igual a (2 * pi * r) / (pi * R ^ 2) = 2 * r / R ^ 2. Isso é muito mais fácil de entender e mais intuitivo, mas não é tão matematicamente correto.


9

Sejam ρ (raio) e φ (azimute) duas variáveis ​​aleatórias correspondentes às coordenadas polares de um ponto arbitrário dentro do círculo. Se os pontos são distribuídos uniformemente, qual é a função de distribuição de ρ e φ?

Para qualquer r: 0 <r <R, a probabilidade da coordenada do raio ρ ser menor que r é

P [ρ <r] = P [ponto está dentro de um círculo do raio r] = S1 / S0 = (r / R) 2

Onde S1 e S0 são as áreas do círculo de raio re R respectivamente. Portanto, o CDF pode ser dado como:

          0          if r<=0
  CDF =   (r/R)**2   if 0 < r <= R
          1          if r > R

E PDF:

PDF = d/dr(CDF) = 2 * (r/R**2) (0 < r <= R).

Observe que para R = 1 variável aleatória sqrt (X) onde X é uniforme em [0, 1) tem esse CDF exato (porque P [sqrt (X) <y] = P [x <y ** 2] = y * * 2 para 0 <y <= 1).

A distribuição de φ é obviamente uniforme de 0 a 2 * π. Agora você pode criar coordenadas polares aleatórias e convertê-las em cartesianas usando equações trigonométricas:

x = ρ * cos(φ)
y = ρ * sin(φ)

Não consigo resistir a publicar código python para R = 1.

from matplotlib import pyplot as plt
import numpy as np

rho = np.sqrt(np.random.uniform(0, 1, 5000))
phi = np.random.uniform(0, 2*np.pi, 5000)

x = rho * np.cos(phi)
y = rho * np.sin(phi)

plt.scatter(x, y, s = 4)

Você vai ter

insira a descrição da imagem aqui


7

Realmente depende do que você quer dizer com 'uniformemente aleatório'. Este é um ponto sutil e você pode ler mais sobre ele na página da wiki aqui: http://en.wikipedia.org/wiki/Bertrand_paradox_%28probability%29 , onde o mesmo problema, dando interpretações diferentes para 'uniformemente aleatório', fornece respostas diferentes!

Dependendo de como você escolhe os pontos, a distribuição pode variar, embora eles sejam uniformemente aleatórios em algum sentido.

Parece que a entrada do blog está tentando torná-la uniformemente aleatória no seguinte sentido: Se você pegar um sub-círculo do círculo, com o mesmo centro, a probabilidade de o ponto cair nessa região é proporcional à área de a região. Isso, acredito, está tentando seguir a interpretação agora padrão de 'uniformemente aleatório' para regiões 2D com áreas definidas : a probabilidade de um ponto cair em qualquer região (com área bem definida) é proporcional à área daquela região.


5
Ou melhor, a probabilidade de que o ponto caia em qualquer região arbitrária é proporcional à área da região - supondo que a região tenha uma área .
precisa

@ Shree: Correto, que é o que eu quis dizer com a minha declaração entre parênteses. Vou deixar mais claro, obrigado. btw, sobre o blog, não havia nenhuma prova real de que áreas arbitrárias fornecem probabilidades proporcionais; portanto, escolhi expressá-lo dessa maneira.

6

Aqui está o meu código Python para gerar numpontos aleatórios a partir de um círculo de raio rad:

import matplotlib.pyplot as plt
import numpy as np
rad = 10
num = 1000

t = np.random.uniform(0.0, 2.0*np.pi, num)
r = rad * np.sqrt(np.random.uniform(0.0, 1.0, num))
x = r * np.cos(t)
y = r * np.sin(t)

plt.plot(x, y, "ro", ms=1)
plt.axis([-15, 15, -15, 15])
plt.show()

1
Por que não apenas r = np.sqrt(np.random.uniform(0.0, rad**2, num))?

4

Penso que, neste caso, o uso de coordenadas polares é uma maneira de complicar o problema, seria muito mais fácil se você escolher pontos aleatórios em um quadrado com lados de comprimento 2R e depois selecionar os pontos de (x,y)modo que x^2+y^2<=R^2.


Você quer dizer x ^ 2 + y ^ 2 <= R ^ 2, eu acho.
SIGFPE

1
Esta é uma amostra de rejeição. Tudo bem, mas significa que o tempo de cálculo varia um pouco, o que pode ser um problema.
Steve Bennett

Todos os quadrados são de 4 lados.
Xaxxon

Esse algoritmo é mais eficiente do que qualquer coisa que envolva raízes quadradas ou cálculos sin / cos. Rejeita menos de 21,5% dos pontos do quadrado.
Ivan Kovtun

3

Solução em Java e o exemplo de distribuição (2000 pontos)

public void getRandomPointInCircle() {
    double t = 2 * Math.PI * Math.random();
    double r = Math.sqrt(Math.random());
    double x = r * Math.cos(t);
    double y = r * Math.sin(t);
    System.out.println(x);
    System.out.println(y);
}

Distribuição 2000 pontos

com base na solução previus https://stackoverflow.com/a/5838055/5224246 from @sigfpe


2

Primeiro, geramos um cdf [x] que é

A probabilidade de um ponto estar menor que a distância x do centro do círculo. Suponha que o círculo tenha um raio de R.

obviamente se x é zero então cdf [0] = 0

obviamente se x é R então o cdf [R] = 1

obviamente se x = r então o cdf [r] = (Pi r ^ 2) / (Pi R ^ 2)

Isso ocorre porque cada "pequena área" do círculo tem a mesma probabilidade de ser escolhida. Portanto, a probabilidade é proporcional à área em questão. E a área dada uma distância x do centro do círculo é Pi r ^ 2

então cdf [x] = x ^ 2 / R ^ 2 porque o Pi se cancela

temos cdf [x] = x ^ 2 / R ^ 2 onde x vai de 0 a R

Então resolvemos para x

R^2 cdf[x] = x^2

x = R Sqrt[ cdf[x] ]

Agora podemos substituir o cdf por um número aleatório de 0 a 1

x = R Sqrt[ RandomReal[{0,1}] ]

Finalmente

r = R Sqrt[  RandomReal[{0,1}] ];
theta = 360 deg * RandomReal[{0,1}];
{r,theta}

obtemos as coordenadas polares {0,601168 R, 311,915 graus}


1

Há uma relação linear entre o raio e o número de pontos "próximos" desse raio; portanto, ele precisa usar uma distribuição de raio que também torne o número de pontos de dados próximo de um raio rproporcional a r.


1

Eu usei uma vez este método: Isso pode ser totalmente não otimizado (ou seja, usa uma matriz de pontos que é inutilizável para grandes círculos), mas fornece distribuição aleatória suficiente. Você pode pular a criação da matriz e desenhar diretamente, se desejar. O método é aleatoriamente todos os pontos em um retângulo que caem dentro do círculo.

bool[,] getMatrix(System.Drawing.Rectangle r) {
    bool[,] matrix = new bool[r.Width, r.Height];
    return matrix;
}

void fillMatrix(ref bool[,] matrix, Vector center) {
    double radius = center.X;
    Random r = new Random();
    for (int y = 0; y < matrix.GetLength(0); y++) {
        for (int x = 0; x < matrix.GetLength(1); x++)
        {
            double distance = (center - new Vector(x, y)).Length;
            if (distance < radius) {
                matrix[x, y] = r.NextDouble() > 0.5;
            }
        }
    }

}

private void drawMatrix(Vector centerPoint, double radius, bool[,] matrix) {
    var g = this.CreateGraphics();

    Bitmap pixel = new Bitmap(1,1);
    pixel.SetPixel(0, 0, Color.Black);

    for (int y = 0; y < matrix.GetLength(0); y++)
    {
        for (int x = 0; x < matrix.GetLength(1); x++)
        {
            if (matrix[x, y]) {
                g.DrawImage(pixel, new PointF((float)(centerPoint.X - radius + x), (float)(centerPoint.Y - radius + y)));
            }
        }
    }

    g.Dispose();
}

private void button1_Click(object sender, EventArgs e)
{
    System.Drawing.Rectangle r = new System.Drawing.Rectangle(100,100,200,200);
    double radius = r.Width / 2;
    Vector center = new Vector(r.Left + radius, r.Top + radius);
    Vector normalizedCenter = new Vector(radius, radius);
    bool[,] matrix = getMatrix(r);
    fillMatrix(ref matrix, normalizedCenter);
    drawMatrix(center, radius, matrix);
}

insira a descrição da imagem aqui


3
As distribuições não são "aleatórias o suficiente". Eles são aleatórios ou não para uma determinada definição de aleatório. Sua resposta é oblíqua: você não comenta seu código nem explica como chegar a ele. Respostas oblíquas são difíceis de seguir e mais difíceis de confiar.
Richard

1

O elemento de área em um círculo é dA = rdr * dphi. Esse fator extra r destruiu sua ideia de escolher aleatoriamente ar e phi. Enquanto o phi é distribuído horizontalmente, r não é, mas horizontalmente em 1 / r (ou seja, é mais provável que você atinja o limite do que "o alvo").

Portanto, para gerar pontos distribuídos uniformemente sobre o círculo, escolha phi a partir de uma distribuição plana er de uma distribuição 1 / r.

Como alternativa, use o método Monte Carlo proposto por Mehrdad.

EDITAR

Para escolher um plano r aleatório em 1 / r, você pode escolher um x aleatório no intervalo [1 / R, infinito] e calcular r = 1 / x. r é então distribuído horizontalmente em 1 / r.

Para calcular um phi aleatório, escolha um x aleatório no intervalo [0, 1] e calcule phi = 2 * pi * x.


Como exatamente eu escolho um r de "uma distribuição 1 / r" ?
precisa saber é

0

Não sei se essa pergunta ainda está aberta para uma nova solução com toda a resposta já dada, mas, por acaso, eu mesma enfrentei exatamente a mesma pergunta. Tentei "raciocinar" comigo mesmo por uma solução e encontrei uma. Pode ser a mesma coisa que alguns já sugeriram aqui, mas de qualquer maneira aqui está:

para que dois elementos da superfície do círculo sejam iguais, assumindo drs iguais, devemos ter dtheta1 / dtheta2 = r2 / r1. Escrevendo a expressão da probabilidade desse elemento como P (r, teta) = P {r1 <r <r1 + dr, teta1 <teta <teta + dtheta1} = f (r, teta) * dr * dtheta1 e definindo os dois probabilidades (para r1 e r2) iguais, chegamos a (assumindo que r e teta são independentes) f (r1) / r1 = f (r2) / r2 = constante, o que fornece f (r) = c * r. E o restante, determinando a constante c, segue a condição em f (r) como um PDF.


Abordagem interessante para começar com dtheta1 / dtheta2 = r2 / r1. Você poderia elaborar como surgiu essa equação?
aioobe

Como outros já mencionaram (buzina, por exemplo), um elemento diferencial da superfície de um círculo é dado como r dr dtheta, portanto, se assumirmos r1 = r2, teremos dr1 * dtheta1 = dr2 * dtheta2 e o restante segue .
ArsaKasra

0

Uma solução de programador:

  • Crie um mapa de bits (uma matriz de valores booleanos). Pode ser tão grande quanto você desejar.
  • Desenhe um círculo nesse mapa de bits.
  • Crie uma tabela de pesquisa dos pontos do círculo.
  • Escolha um índice aleatório nesta tabela de pesquisa.
const int RADIUS = 64;
const int MATRIX_SIZE = RADIUS * 2;

bool matrix[MATRIX_SIZE][MATRIX_SIZE] = {0};

struct Point { int x; int y; };

Point lookupTable[MATRIX_SIZE * MATRIX_SIZE];

void init()
{
  int numberOfOnBits = 0;

  for (int x = 0 ; x < MATRIX_SIZE ; ++x)
  {
    for (int y = 0 ; y < MATRIX_SIZE ; ++y)
    {
      if (x * x + y * y < RADIUS * RADIUS) 
      {
        matrix[x][y] = true;

        loopUpTable[numberOfOnBits].x = x;
        loopUpTable[numberOfOnBits].y = y;

        ++numberOfOnBits;

      } // if
    } // for
  } // for
} // ()

Point choose()
{
  int randomIndex = randomInt(numberOfBits);

  return loopUpTable[randomIndex];
} // ()

O bitmap é necessário apenas para a explicação da lógica. Este é o código sem o bitmap:

const int RADIUS = 64;
const int MATRIX_SIZE = RADIUS * 2;

struct Point { int x; int y; };

Point lookupTable[MATRIX_SIZE * MATRIX_SIZE];

void init()
{
  int numberOfOnBits = 0;

  for (int x = 0 ; x < MATRIX_SIZE ; ++x)
  {
    for (int y = 0 ; y < MATRIX_SIZE ; ++y)
    {
      if (x * x + y * y < RADIUS * RADIUS) 
      {
        loopUpTable[numberOfOnBits].x = x;
        loopUpTable[numberOfOnBits].y = y;

        ++numberOfOnBits;
      } // if
    } // for
  } // for
} // ()

Point choose()
{
  int randomIndex = randomInt(numberOfBits);

  return loopUpTable[randomIndex];
} // ()

0

Ainda não tenho certeza sobre o exato '(2 / R2) × r', mas o que é aparente é o número de pontos a serem distribuídos na unidade 'dr', ou seja, o aumento em r será proporcional a r2 e não a r.

marque desta maneira ... o número de pontos em algum ângulo teta e entre r (0,1r a 0,2r), ou seja, a fração de re número de pontos entre r (0,6r a 0,7r) seria igual se você usar a geração padrão, já que a diferença é de apenas 0,1r entre dois intervalos. mas como a área coberta entre pontos (0.6r a 0.7r) será muito maior do que a área coberta entre 0.1r e 0.2r, o número igual de pontos será espaçado esparsamente em uma área maior, isso eu assumo que você já saiba, então a função para gerar os pontos aleatórios não deve ser linear, mas quadrático (já que o número de pontos a serem distribuídos em determinada unidade 'dr', isto é, o aumento em r será proporcional a r2 e não r), portanto, neste caso, será inverso de quadrático, desde o delta que temos (0.


Você é o primeiro a fazer referência ao teorema de Pitágoras aqui. Eu adoraria se você pudesse expandir isso com uma ou duas figuras, apoiando sua explicação. Eu tenho dificuldade em seguir como está agora :-(
aioobe 16/17/17

@aioobe Tentei reformular a resposta, eu posso adicionar diagramas se precisar :)
cheesefest

Entendo por que não posso espalhá-lo linearmente. O que não entendo aqui é a conexão com Pitágoras ou com o pecado / cos. Talvez diagramas possam me ajudar aqui.
aioobe

Pitágoras é o meu erro, por favor, esqueça-o, mas espero que você tenha entendido a natureza quadrática da função, o exato (2 / R2) × r precisa de provas e eu não consigo encontrar nenhuma prova disso
cheesefest

0

Um problema tão divertido.
A lógica da probabilidade de um ponto ser escolhido diminuindo à medida que a distância da origem do eixo aumenta é explicada várias vezes acima. Nós explicamos isso tomando a raiz de U [0,1]. Aqui está uma solução geral para um r positivo no Python 3.

import numpy
import math
import matplotlib.pyplot as plt

def sq_point_in_circle(r):
    """
    Generate a random point in an r radius circle 
    centered around the start of the axis
    """

    t = 2*math.pi*numpy.random.uniform()
    R = (numpy.random.uniform(0,1) ** 0.5) * r

    return(R*math.cos(t), R*math.sin(t))

R = 200 # Radius
N = 1000 # Samples

points = numpy.array([sq_point_in_circle(R) for i in range(N)])
plt.scatter(points[:, 0], points[:,1])

insira a descrição da imagem aqui


0

Você também pode usar sua intuição.

A área de um círculo é pi*r^2

Para r=1

Isso nos dá uma área de pi. Vamos assumir que temos algum tipo de função fque distribuiria uniformemente os N=10pontos dentro de um círculo. A proporção aqui é10 / pi

Agora dobramos a área e o número de pontos

Para r=2eN=20

Isso fornece uma área de 4pie a proporção é agora 20/4piou 10/2pi. A proporção ficará menor e menor quanto maior o raio, porque seu crescimento é quadrático e as Nescalas linearmente.

Para consertar isso, podemos apenas dizer

x = r^2
sqrt(x) = r

Se você gerasse um vetor em coordenadas polares como esta

length = random_0_1();
angle = random_0_2pi();

Mais pontos chegariam ao centro.

length = sqrt(random_0_1());
angle = random_0_2pi();

length não está mais uniformemente distribuído, mas o vetor agora será distribuído uniformemente.


-1

1) Escolha um X aleatório entre -1 e 1.

var X:Number = Math.random() * 2 - 1;

2) Usando a fórmula do círculo, calcule os valores máximo e mínimo de Y, dado que X e um raio de 1:

var YMin:Number = -Math.sqrt(1 - X * X);
var YMax:Number = Math.sqrt(1 - X * X);

3) Escolha um Y aleatório entre os extremos:

var Y:Number = Math.random() * (YMax - YMin) + YMin;

4) Incorpore seus valores de localização e raio no valor final:

var finalX:Number = X * radius + pos.x;
var finalY:Number = Y * radois + pos.y;

2
Não uniforme - a probabilidade para [-1, 0] é muito maior que para [0, 0], dado que p ([- 1, Y]) = p ([0, Y]), e existe apenas uma única escolha para [-1, Y] e muitas opções para [0, Y].
Amadan 12/06

Esta solução favorece pontos para os lados esquerdo e direito do círculo. Os pontos com x próximos a zero estão sub-representados. Não é uma distribuição uniforme.
Dawood ibn Kareem
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.