Randomize pontos em um disco


14

Eu li sobre círculos em algum lugar e agora aprendi sobre discos ( na verdade é um conceito bastante comum ) e pensei sobre codegolf.

Sua tarefa é aleatoriamente um ponto / vários pontos em um disco com o raio 1.

Regras:

  • Todos os pontos devem ter igual probabilidade de serem gerados
  • Coordenadas de ponto flutuante devem ser usadas; o requisito mínimo é de duas casas decimais (por exemplo, os pontos (0.12, -0.45)ou (0.00, -1.00)são válidos)
  • Você recebe -20 bytes se o seu programa realmente exibir o círculo delimitador e os pontos gerados nele. As coordenadas ainda precisam ser válidas, mas não exibidas, e a imagem gerada deve ter pelo menos 201 por 201 pixels de tamanho
  • Você obtém -5 bytes se seu programa usa o número de pontos a serem gerados como entrada no stdin
  • Se você decidir não plotar o círculo delimitador e o (s) ponto (s), seu programa precisará gerar o (s) ponto (s) gerado (s) no formato (x, y)ou (x,y)no stdout
  • Se você decidir usar o número de pontos gerados como entrada, mas não plotá-lo - o seu programa precisará gerar todos os pontos aleatórios no formato indicado acima, com ou sem um espaço entre eles.

Menor envio em bytes ganha!


1
@sweerpotato Sim, especifique que todos os pontos no círculo são válidos. Eu não sabia que você queria dizer os dois. Além disso, parece que essa pergunta se encaixaria melhor em um desafio de código-golfe do que em um concurso de popularidade, mas essa é apenas a minha opinião.
Cole

5
" Faça XYZ de maneira criativa " é o clássico Bad Popcon Question ™. O que uma pessoa considera criativa é o que outra pessoa considera da maneira óbvia.
Peter Taylor

Por curiosidade, por que um requisito de saída de pixels de 201x201 para plotagens?
Johne

@JohnE sugeri 201x201 pixels, uma vez que corresponde ao lugar precisão 2 decimal necessário
Trichoplax

Podemos emitir as coordenadas como números complexos? Por exemplo: 0.3503082505747327+0.13499221288682994j.
orlp

Respostas:


5

Pitão, 26-5 = 21 bytes

VQp(sJ*@OZ2^.n1*yOZ.l_1)eJ

Pega o número de coordenadas a serem geradas no stdin e as gera no stdout da seguinte forma:

(-0.5260190768964058, -0.43631187015380823)(-0.12127959509302746, -0.08556306418467638)(-0.26813756369750996, -0.4564539715526493)

Usa uma estratégia semelhante ao @ MartinBüttner, gerando coordenadas polares e raios, exceto que o faz usando exponenciação complexa.


Você pode remover o p, não pode? Apenas altera a saída para linhas separadas.
PurkkaKoodari

@ Pietu1998 Isso não é permitido, veja os comentários sobre a questão principal.
orlp 7/09/15

Oh tudo bem.
PurkkaKoodari

16

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 re 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 1ser 4 vezes mais comuns que 0e duas vezes mais comuns que 2; queremos 3três vezes mais comuns que 0):

insira a descrição da imagem aqui

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:

insira a descrição da imagem aqui

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:

insira a descrição da imagem aqui

E agora tratamos a linha superior deste gráfico como uma função f(x) = ye a invertemos para obter uma função , que podemos aplicar a um valor aleatoriamente uniforme em :g(y) = f-1(y) = xy ∈ [0,1]

insira a descrição da imagem aqui

Legal, então como usar isso para gerar uma distribuição linear de raios? Esta é a distribuição que queremos:

insira a descrição da imagem aqui

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 0para 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 r21rr2

insira a descrição da imagem aqui

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 yestá o valor aleatório:

insira a descrição da imagem aqui

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.


1
Explicação muito agradável!
Sweerpotato

2
Mmm pictures: D
Beta Decay

12

Mathematica, 68 44 - 20 = 24 bytes

Muito obrigado a David Carraher por me informar RandomPoint, que economizou 24 (!) Bytes. Mathematica não tem um built-in para tudo.

Graphics@{Circle[],Point@RandomPoint@Disk[]}

Isso representa o ponto e o círculo delimitador para se qualificar para o bônus:

insira a descrição da imagem aqui

O resultado é uma imagem vetorial; portanto, a especificação de tamanho de 201x201 pixels não faz muito sentido, mas, por padrão, é maior que isso.


Que tal Graphics[{Circle[], Point@RandomPoint@Disk[]}]?
DavidC

Seja meu convidado. Além disso, para salvar 1 byte ... #Graphics@{Circle[], Point@RandomPoint@Disk[]}
6154

@DavidCarraher Muito obrigado! :)
Martin Ender

Não conheço a sintaxe do Mathematica, mas certamente você pode salvar outro byte removendo o espaço após o ,?
macio

@fluffy eu já fiz na versão postada
Martin Ender

9

CJam, 31 26 bytes

{];'({2dmr(_}2*@mhi}g',\')

Isso funciona gerando repetidamente pontos aleatórios em um quadrado de comprimento lateral 2 e mantendo o primeiro que cai dentro do disco da unidade.

Obrigado a @ MartinBüttner por jogar fora 3 bytes!

Experimente on-line no intérprete CJam .

Como funciona

{                  }g       Do:
 ];'(                         Clear the stack and push a left parenthesis.
     {      }2*               Do twice:
      2dmr                      Randomly select a Double between 0 and 2.
          (_                    Subtract 1 and push a copy.
               @              Rotate the copy of the first on top of the stack.
                mh            Compute the Euclidean norm of the vector consisting
                              of the two topmost Doubles on the stack.
                  i           Cast to integer.
                            If the result is non-zero, repeat the loop.
                     ',\    Insert a comma between the Doubles.
                        ')  Push a right parenthesis.

8

iKe , 53 51 bytes

Nada de especial, mas acho que devemos ter pelo menos uma solução gráfica:

,(80+160*t@&{.5>%(x*x)+y*y}.+t:0N 2#-.5+?9999;cga;3)

enredo

Experimente no seu navegador .

Edit: Eu posso raspar dois bytes aplicando a abordagem de @ MartinBüttner para modificar a distribuição de coordenadas polares. Eu acho que também é um pouco mais direto:

,(80*1+(%?c){x*(cos y;sin y)}'6.282*?c:9999;cga;3)

3
Se você também desenhar o círculo delimitador, você se qualificará para -20.
orlp 6/09/15

1
O iKe possui um modelo de desenho baseado em varredura, tornando esse requisito injusto. Eu acho que custaria um pouco mais de 20 caracteres para renderizar uma aproximação de um círculo delimitador também.
Johne

7

Perl, 59 bytes

while(($x=1-rand 2)**2+($y=1-rand 2)**2>1){};print"($x,$y)"

Esta é apenas uma solução simples, gerando pontos em um quadrado e rejeitando aqueles muito distantes. Meu truque de golfe singular é incluir as tarefas dentro da condição.

Edit: No processo de golfe, eu achei uma maneira interessante de imprimir pontos aleatórios em um círculo .

use Math::Trig;$_=rand 2*pi;print"(",sin,",",cos,")"

7

Oitava, 24 53 - 20 = 33 bytes

polar([0:2e-3:1,rand]*2*pi,[ones(1,501),rand^.5],'.')

Gera 501 valores teta igualmente espaçados mais um número aleatório e dimensiona todos eles para [0..2π]. Em seguida, gera 501 1 para o raio do círculo, mais um raio aleatório para o ponto e pega a raiz quadrada para garantir uma distribuição uniforme sobre o disco. Em seguida, plota todos os pontos como coordenadas polares.

insira a descrição da imagem aqui


Aqui está uma demonstração rápida da distribuição (sem o círculo unitário):

polar(2*pi*rand(99),rand(99).^.5,'.')

9801 Pontos


5

Oitava / Matlab, 74 64 bytes

Método de rejeição , 64 bytes:

u=1;v=1;while u^2+v^2>1
u=rand;v=rand;end
sprintf('(%f,%f)',u,v)

Método direto , 74 bytes (obrigado a Martin Büttner por me ajudar a corrigir dois erros):

t=rand*2*pi;r=1-abs(1-sum(rand(2,1)));sprintf('(%f,%f)',r*cos(t),r*sin(t))

5

R, 99 95 81-20 = 79 75 61 bytes

symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))

Use a construção de números complexos para construir os x / ys a partir de coordenadas polares. Receber a entrada foi um pouco caro e provavelmente existe uma maneira melhor de fazer isso. O ylim exlim é para garantir que todo o círculo seja plotado e oasp garante que os pontos sejam mostrados sob o símbolo do círculo.

Graças a @jbaums e @flodel pela economia

Experimente aqui


runif(9,0,1)pode ser simplificado pararunif(9)
jbaums

@jbaums, graças ... uma das coisas que eu sempre parecem esquecer :)
MickyT

Pode barbear 14:symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))
flodel

@flodel muito bom obrigado.
MickyT

Outra pequena economia: ylitrabalha no lugar de ylim.
jbaums

4

Processando / Java 141 bytes-20 = 121

o requisito de tamanho mínimo de 201 * 201 exige que eu insira o setupmétodo, já que o Processing.org padroniza 200x200 :(

void setup(){noFill();size(201,201);}void draw(){float f=10,a=PI*2*random(),r=random();point(f+f*sin(a)*r,f+f*cos(a)*r);ellipse(f,f,f*2,f*2)}

Eu não sabia que o processamento / java era permitido, puro!
perfil completo de J Atkin

4

QBasic, 138 bytes - 20 - 5 = 113

INPUT n
r=200
SCREEN 12
RANDOMIZE TIMER
CIRCLE(r,r),r
PAINT(r,r)
FOR i=1TO n
DO
x=RND*r*2
y=RND*r*2
LOOP UNTIL POINT(x,y)
PSET(x,y),1
NEXT

Pega a entrada do usuário e desenha o disco e os pontos. Testado em QB64 .

Esta é uma estratégia bastante básica "jogue no alvo e mantenha o que fica". O problema é que "o que cola" não é determinado matematicamente, mas graficamente: um disco branco é plotado em um fundo preto e os pontos gerados aleatoriamente são rejeitados até que não sejam pretos. Os pontos em si são desenhados em azul (embora seja difícil dizer quando são pixels únicos - clique na imagem para ampliar).


3

awk - 95-5 = 90

{
    for(;$1--;printf"("(rand()<.5?x:-x)","(rand()<.5?y:-y)")")
        while(1<(x=rand())^2+(y=rand())^2);
}

Como não tinha muita certeza da parte rand () <. 5, fiz alguns testes de distribuição com isso, usando este script:

BEGIN{ srand() }
{ 
    split("0 0 0 0", s)
    split("0 0 0 0", a)

    for(i=$1; i--; )
    {
        while( 1 < r2 = ( x=rand() )^2 + ( y=rand() )^2 );

        x = rand()<.5 ? x : -x
        y = rand()<.5 ? y : -y

        ++s[ x>=0 ? y>=0 ? 1 : 4 : y>=0 ? 2 : 3 ]

        ++a[ r2>.75 ? 1 : r2>.5 ? 2 : r2>.25 ? 3 : 4]
    }

    print "sector distribution:"
        for(i in s) print "sector " i ": " s[i]/$1

    print "quarter area distribution:"
        for(i in a) print "ring " i ":   " a[i]/$1
}

que para uma entrada de 1e7 me dá esse resultado, depois de tomar uma ou duas vezes no meu café:

1e7
sector distribution:
sector 1: 0.250167
sector 2: 0.249921
sector 3: 0.249964
sector 4: 0.249948
quarter area distribution:
ring 1:   0.24996
ring 2:   0.25002
ring 3:   0.250071
ring 4:   0.249949

o que eu acho que está bem.

Uma pequena explicação:
depois de rabiscar por um tempo, que se você quiser dividir o disco em quatro anéis com a mesma área, os raios nos quais você precisaria cortar são sqrt (1/4), sqrt (1/2 ) e sqrt (3/4). Como o raio real do ponto que eu teste seria sqrt (x ^ 2 + y ^ 2), eu posso pular a raiz quadrada todos juntos. A 1/4, 2/4, 3/4 "coincidência" pode estar relacionada ao que M. Buettner apontou anteriormente.


3

HPPPL , 146 (171-20-5) bytes

EXPORT r(n)BEGIN LOCAL R,A,i,Q;RECT();Q:=118.;ARC_P(Q,Q,Q);FOR i FROM 1 TO n DO R:=√RANDOM(1.);A:=RANDOM(2*π);PIXON_P(G0,IP(Q+Q*R*COS(A)),IP(Q+Q*R*SIN(A)));END;FREEZE;END;

Exemplo para 10000 pontos (incluindo o tempo em segundos para o dispositivo real):

Randomize pontos em um disco, cronometrando

A função em si é chamada por r(n) . O restante da imagem acima é apenas para fins de tempo.

Resultado (o diâmetro do disco é 236 pixels):

insira a descrição da imagem aqui

A versão acima não armazena as coordenadas do ponto, então escrevi uma versão que usa dois parâmetros r(n,p). né o número de pontos e p=0retorna os pontos ao terminal, p=1plota os pontos e o disco), caso o armazenamento de coordenadas seja obrigatório. Esta versão tem 283 (308-20-5) bytes de comprimento:

EXPORT r(n,p)BEGIN LOCAL R,A,j,Q,x,y;Q:=118.0;CASE IF p==0 THEN print() END IF p==1 THEN RECT();ARC_P(Q,Q,Q) END END;FOR j FROM 1 TO n DO R:=√RANDOM(1.0);A:=RANDOM(2*π);x:=R*COS(A);y:=R*SIN(A);CASE IF p==0 THEN print("("+x+", "+y+")") END IF p==1 THEN PIXON_P(G0,IP(Q+Q*x),IP(Q+Q*y)) END END;END;FREEZE;END;

A versão não destruída:

EXPORT r(n,p)
BEGIN
LOCAL R,A,j,Q,x,y;
  Q:=118.0;
  CASE
    IF p==0 THEN print() END
    IF p==1 THEN RECT();ARC_P(Q,Q,Q) END
  END;
  FOR j FROM 1 TO n DO
    R:=√RANDOM(1.0);
    A:=RANDOM(2*π);
    x:=R*COS(A);
    y:=R*SIN(A);
    CASE
      IF p==0 THEN print("("+x+", "+y+")") END
      IF p==1 THEN PIXON_P(G0,IP(Q+Q*x),IP(Q+Q*y)) END
    END;
  END;
  FREEZE;
END;

Saída terminal para r(10,0):

Randomize pontos em uma saída do terminal de disco

r(10,1) mostra o disco com os pontos, como mostrado acima.


2

JavaScript, 75 bytes

Baseado em rejeição:

do x=(r=()=>4*Math.random()-2)(),y=r()
while(x*x+y*y>1)
alert(`(${[x,y]})`)

Método direto (80 bytes):

alert(`(${[(z=(m=Math).sqrt((r=m.random)()))*m.sin(p=m.PI*2*r()),z*m.cos(p)]})`)

2

Python, 135 130 bytes

from random import*
def r():return uniform(-1,1)
p=[]
while not p:
    x,y=r(),r()
    if x**2+y**2<=1:p=x,y
print'(%.2f, %2f)'%p

Removido o **0.5agradecimento à sugestão de @ jimmy23013 (por ser um círculo unitário, agora estou verificando se a distância ao quadrado entre (x, y) e (0, 0) é igual a 1 2. É a mesma coisa.

Isso também me liberou para remover os parênteses.


Eu acho que você não precisa do **0.5.
precisa saber é o seguinte

@ jimmy23013 Obrigado! removido.
JF
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.