Golfe Aleatório do Dia # 4: O Paradoxo de Bertrand

19

Sobre a série

Primeiro, você pode tratar isso como qualquer outro desafio de código de golfe e respondê-lo sem se preocupar com a série. No entanto, existe uma tabela de classificação em todos os desafios. Você pode encontrar o placar junto com mais informações sobre a série no primeiro post .

Embora eu tenha várias idéias alinhadas para a série, os desafios futuros ainda não estão definidos. Se você tiver alguma sugestão, informe-me na postagem da sandbox relevante .

Buraco 4: O Paradoxo de Bertrand

O paradoxo de Bertrand é um problema interessante, que mostra como diferentes métodos para escolher acordes aleatórios em um círculo podem produzir distribuições diferentes de acordes, seus pontos médios e seus comprimentos.

Neste desafio, você deve gerar acordes aleatórios do círculo unitário, usando o método "correto", isto é, um que produz uma distribuição de acordes invariável em escala e conversão. No artigo vinculado da Wikipedia, "Método 2" é esse método.

Aqui estão as regras exatas:

  • Você deve pegar um número inteiro positivoN que especifique quantos acordes devem ser retornados. A saída deve ser uma lista de Nacordes, cada um especificado como dois pontos no círculo unitário, dados pelo ângulo polar em radianos.
  • Seu código deve poder retornar pelo menos 2 20 valores diferentes para cada um dos dois ângulos . Se o seu RNG disponível tiver um intervalo menor, você deve primeiro criar um RNG com um intervalo suficientemente grande em cima do incorporado ou implementar o seu próprio RNG adequado . Esta página pode ser útil para isso.
  • A distribuição dos acordes deve ser indistinguível da produzida pelo "Método 2" no artigo vinculado da Wikipedia. Se você implementar um algoritmo diferente para escolher acordes, inclua uma prova de correção. Qualquer que seja o algoritmo que você escolha implementar, teoricamente deve ser capaz de gerar qualquer acorde válido no círculo de unidades (limitações de restrição do PRNG subjacente ou tipos de dados de precisão limitada).
  • Sua implementação deve usar e retornar números de ponto flutuante (pelo menos 32 bits de largura) ou números de ponto fixo (pelo menos 24 bits de largura) e todas as operações aritméticas devem ser precisas em no máximo 16 ulp .

Você pode escrever um programa completo ou uma função e obter entrada via STDIN (ou alternativa mais próxima), argumento da linha de comando ou argumento da função e produzir saída via STDOUT (ou alternativa mais próxima), valor de retorno da função ou parâmetro da função (saída).

A saída pode estar em qualquer formato conveniente de lista ou string, desde que os números individuais sejam claramente distinguíveis e seu número total seja sempre par.

Isso é código de golfe, então a submissão mais curta (em bytes) vence. E, é claro, o menor envio por usuário também entrará na tabela geral de líderes da série.

Visualização

Você pode usar o seguinte snippet para renderizar as linhas geradas e inspecionar sua distribuição. Basta colar uma lista de pares de ângulos na área de texto. O trecho deve ser capaz de lidar com quase qualquer formato de lista, desde que os números sejam simples números decimais (sem notação científica). Eu recomendo que você use pelo menos 1000 linhas para ter uma boa idéia da distribuição. Também forneci alguns dados de exemplo para os diferentes métodos apresentados no artigo abaixo.

Dados de exemplo gerados com o método 1.

Dados de exemplo gerados com o método 2.

Dados de exemplo gerados com o método 3.

Entre os melhores

O primeiro post da série gera uma tabela de classificação.

Para garantir que suas respostas sejam exibidas, inicie todas as respostas com um título, usando o seguinte modelo de remarcação:

# Language Name, N bytes

onde Nestá o tamanho do seu envio. Se você melhorar sua pontuação, poderá manter as pontuações antigas no título, identificando-as. Por exemplo:

# Ruby, <s>104</s> <s>101</s> 96 bytes

(O idioma não é exibido no momento, mas o snippet exige e o analisa, e posso adicionar um cabeçalho por idioma no futuro.)

Martin Ender
fonte

Respostas:

12

Código de máquina IA-32, 54 bytes

Hexdump do código:

68 00 00 00 4f 0f c7 f0 50 db 04 24 58 d8 34 24
f7 d9 78 f1 d9 c0 dc c8 d9 e8 de e1 d9 fa d9 c9
d9 f3 dc c0 d9 eb de ca d8 c1 dd 1a dd 5a 08 83
c2 10 e2 d1 58 c3

Ele usa um algoritmo (ligeiramente modificado) que a Wikipedia descreveu. No pseudo-código:

x = rand_uniform(-1, 1)
y = rand_uniform(-1, 1)
output2 = pi * y
output1 = output2 + 2 * acos(x)

Eu uso o intervalo -1...1porque é fácil criar números aleatórios nesse intervalo: a rdrandinstrução gera um número inteiro entre -2^31e2^31-1 , que pode ser facilmente dividido por 2 ^ 31.

Eu deveria ter usado o intervalo 0...1para o outro número aleatório (x), que é alimentadoacos ; no entanto, a parte negativa é simétrica com a parte positiva - números negativos produzem acordes cujo intervalo é maior que pi radianos, mas com o objetivo de ilustrar o paradoxo de Bertrand, isso não importa.

Como o conjunto de instruções 80386 (ou x87) não possui um acos instrução , tive que expressar o cálculo usando apenas a ataninstrução:

acos(x) = atan(sqrt(1-x^2)/x)

Aqui está o código fonte que gera o código da máquina acima:

__declspec(naked) void __fastcall doit1(int n, std::pair<double, double>* output)
{
    _asm {
        push 0x4f000000;        // [esp] = float representation of 2^32

    myloop:
        rdrand eax;             // generate a random number, -2^31...2^31-1
        push eax;               // convert it
        fild dword ptr [esp];   // to floating-point
        pop eax;                // restore esp
        fdiv dword ptr [esp];   // convert to range -1...1
        neg ecx;
        js myloop;              // do the above 2 times

        // FPU stack contents:  // x           | y
        fld st(0);              // x           | x   | y
        fmul st(0), st;         // x^2         | x   | y
        fld1;                   // 1           | x^2 | x | y
        fsubrp st(1), st;       // 1-x^2       | x   | y
        fsqrt;                  // sqrt(1-x^2) | x   | y
        fxch;                   // x           | sqrt(1-x^2) | y
        fpatan;                 // acos(x)     | y
        fadd st, st(0);         // 2*acos(x)   | y
        fldpi;                  // pi          | 2*acos(x) | y
        fmulp st(2), st;        // 2*acos(x)   | pi*y
        fadd st, st(1);         // output1     | output2
        fstp qword ptr [edx];   // store the numbers
        fstp qword ptr [edx + 8];

        add edx, 16;            // advance the output pointer
        loop myloop;            // loop

        pop eax;                // restore stack pointer
        ret;                    // return
    }
}

Para gerar dois números aleatórios, o código usa um loop aninhado. Para organizar o loop, o código tira proveito do ecxregistro positivo (contador de loop externo). Ele nega temporariamente ecxe o faz novamente para restaurar o valor original. A jsinstrução repete o loop quando ecxé negativo (isso acontece exatamente uma vez).

anatolyg
fonte
Eu gosto desta resposta para usar o assembly IA32! Só para dizer: este não é estritamente 386 código de máquina como 80386, obviamente, não têm instrução rdrand nem necessário um FP coprocessador
user5572685
Sim, IA32 é um nome melhor. Não específica o suficiente, mas provavelmente mais correto do que 80386.
anatolyg
7

Pyth, 25 23 22 bytes

Uma porta da resposta C ++ 11 da rcrmn. Este é o meu primeiro uso do Pyth, e me diverti bastante!

VQJ,*y.n0O0.tOZ4,sJ-FJ

Versão de 23 bytes:

VQJ*y.n0O0K.tOZ4+JK-JKd

Corte um byte alterando o programa para usar dobras + somas e definindo J como uma tupla, removendo K.

Original:

VQJ**2.n0O0K.tO0 4+JK-JKd

Corte 2 bytes graças a @orlp.

Explicação:

VQ                         loop as many times as the input number
  J,                       set J to the following tuple expression
    *y.n0O0                2 * .n0 (pi) * O0 (a random number between 0 and 1)
            .tOZ4          .tOZ 4 (acos of OZ (a random number))
                 ,sJ-FJ    print the sum of J and folding J using subtraction in parenthesis, separated by a comma, followed by another newline
kirbyfan64sos
fonte
11
Dicas de Pyth: *2_é o mesmo que y_. A variável Zé inicialmente 0, para que você possa remover o espaço .tO0 4escrevendo .tOZ4.
orlp
11
Estou contando 25 bytes ...
Maltysen
Além disso, você pode formatar melhor a saída com,+JK-JK
Maltysen
@ Maltysen Ambos corrigidos. Obrigado!
Kirbyfan64sos
@ orlp Eu não tinha idéia ye esqueci Z. Fixo; obrigado!
Kirbyfan64sos
6

Julia, 48 bytes

n->(x=2π*rand(n);y=acos(rand(n));hcat(x+y,x-y))

Isso usa o algoritmo do método 2, como a maioria das respostas até agora. Ele cria uma função lambda que recebe uma entrada inteira e retorna uma matriz nx 2. Para chamá-lo, dê um nome, por exemplo f=n->....

Ungolfed + explicação:

function f(n::Int64)
    # The rand() function returns uniform random numbers using
    # the Mersenne-Twister algorithm

    # Get n random chord angles
    x = 2π*rand(n)

    # Get n random rotations
    y = acos(rand(n))

    # Bind into a 2D array
    hcat(x+y, x-y)
end

Eu realmente gosto da aparência das visualizações, então incluirei uma. É o resultado de f(1000).

Círculo

Alex A.
fonte
5

Pitão, 22 bytes

Uma porta da resposta C ++. Eu tinha outra solução de 23 bytes (agora 22!), Mas era quase uma cópia da resposta pyth de @ kirbyfan64sos com otimizações, então tive que pensar fora da caixa um pouco e de forma criativa (ab) usar o operador fold.

m,-Fdsdm,y*.nZOZ.tOZ4Q

Observe que isso não funciona no momento por causa de um bug no operador fold após a introdução de reduce2. Estou fazendo uma solicitação de recebimento.

m             Map    
 ,            Tuple of
  -Fd         Fold subtraction on input
  sd          Fold addition on input
 m      Q     Map over range input
  ,           Tuple           
   y          Double
    *         Product
     .nZ      Pi
     OZ       [0, 1) RNG
  .t  4       Acos
    OZ        [0, 1) RNG

Para refence, essa foi minha outra solução que funciona da mesma maneira: VQKy*.nZOZJ.tOZ4,+KJ-KJ

Maltysen
fonte
Você mispelled meu nome de usuário ... :(
kirbyfan64sos
@ kirbyfan64sos derp. Desculpe;)
Maltysen
4

IDL, 65 bytes

Obviamente, esse é o mesmo algoritmo que o @rcrmn, embora eu tenha derivado de forma independente antes de ler a resposta.

read,n
print,[2,2]#randomu(x,n)*!pi+[-1,1]#acos(randomu(x,n))
end

A função randomu da IDL usa o Mersenne Twister, que possui um período de 2 19937 -1.

Edição: Corri 1000 acordes através do visualizador acima, aqui está uma captura de tela do resultado:

IDL bertrand

sirpercival
fonte
4

C ++ 11, 214 bytes

#include<random>
#include<iostream>
#include<cmath>
int main(){using namespace std;int n;cin>>n;random_device r;uniform_real_distribution<> d;for(;n;--n){float x=2*M_PI*d(r),y=acos(d(r));cout<<x+y<<' '<<x-y<<';';}}

Portanto, esta é uma implementação direta do direito algoritmo na página da wikipedia. O principal problema aqui no golfe são os nomes tão assustadores que as classes de geradores aleatórios têm. Mas, ao contrário do bom e velho, é pelo menos adequadamente uniforme.

Explicação:

#include<random>
#include<iostream>
#include<cmath>
int main()
{
    using namespace std;
    int n;
    cin>>n; // Input number
    random_device r; // Get a random number generator
    uniform_real_distribution<> d;   // Get a uniform distribution of 
                                     // floats between 0 and 1
    for(;n;--n)
    {
        float x = 2*M_PI*d(r),       // x: Chosen radius angle
              y = acos(d(r));        // y: Take the distance from the center and 
                                     // apply it an inverse cosine, to get the rotation

        cout<<x+y<<' '<<x-y<<';';    // Print the two numbers: they are the rotation
                                     // of the radius +/- the rotation extracted from
                                     // the distance to the center
    }
}
rorlork
fonte
11
Esse fator M_PI_2parece suspeito. Eu acho que deveria ser 1 em vez disso.
Anatolyg
Sim, completamente certo, vai corrigi-lo agora! Muito obrigado!
rorlork
4

APL, 46 bytes

f←{U←⍵ 2⍴∊{(○2×?0)(¯1○?0)}¨⍳⍵⋄⍉2⍵⍴∊(+/U)(-/U)}

Meu primeiro programa de APL! Certamente, ele pode ser bastante aprimorado (como minha compreensão geral do APL está ausente), portanto, qualquer sugestão seria fantástica. Isso cria uma funçãof que recebe um número inteiro como entrada, calcula os pares de pontos de acordes usando o método 2 e imprime cada par separado por uma nova linha.

Você pode experimentá-lo online !

Explicação:

f←{ ⍝ Create the function f which takes an argument ⍵

    ⍝ Define U to be an ⍵ x 2 array of pairs, where the first
    ⍝ item is 2 times a random uniform float (?0) times pi (○)
    ⍝ and the second is the arccosine (¯1○) of another random
    ⍝ uniform float.

    U ← ⍵ 2 ⍴ ∊{(○2×?0)(¯1○?0)}¨⍳⍵

    ⍝ Create a 2 x ⍵ array filled with U[;1]+U[;2] (+/U) and
    ⍝ U[;1]-U[;2] (-/U). Transpose it into an ⍵ x 2 array of
    ⍝ chord point pairs and return it.

    ⍉ 2 ⍵ ⍴ ∊(+/U)(-/U)
}

Nota: Minha solução de 19 bytes anterior era inválida, pois retornava (x, y) em vez de (x + y, xy). Tristeza abunda.

Alex A.
fonte
3

Java, 114 bytes

n->{for(;n-->0;){double a=2*Math.PI*Math.random(),b=Math.acos(Math.random());System.out.println(a+b+" "+(a-b));}};

Implementação básica em java. Use como uma expressão lambda.

Exemplo de uso

O número um
fonte
Você não pode reduzir o tamanho armazenando em Mathalgum lugar? Ou alguma coisa? (Im não um programador Java)
Ismael Miguel
@IsmaelMiguel Isso custaria 2 caracteres extras.
TheNumberOne
Desculpe: / É tentador tentar reduzir o número de vezes que Mathaparece. O que a meta diz sobre o uso de um código para gerar outro código para resolver o problema?
Ismael Miguel
2
@IsmaelMiguel Esse é um jogo justo, embora eu fique surpreso se você for realmente melhor em metagolfing do que em golfe.
Martin Ender
3

Ruby, 72 bytes

Meu primeiro golfe aqui! Eu usei o mesmo código de todos, espero que esteja tudo bem

gets.chomp.to_i.times{puts"#{x=2*Math::PI*rand},#{x+2*Math.acos(rand)}"}
rorlok
fonte
2

Java, 115 123

Isso é basicamente o mesmo que a maioria dos outros, mas eu preciso de uma pontuação Java para esse buraco, então aqui vai:

void i(int n){for(double x;n-->0;System.out.println(x+2*Math.acos(Math.random())+" "+x))x=2*Math.PI*Math.random();}

Podem ser encontrados 1000 acordes de amostra em pastebin , eis os cinco primeiros de uma execução:

8.147304676211474 3.772704020731153
8.201346559916786 3.4066194978900106
4.655131524088468 2.887965593766409
4.710707820868578 3.8493686706403984
3.3839198612642423 1.1604092552846672
Geobits
fonte
1

CJam, 24 22 bytes

Semelhante a outros algoritmos, aqui está uma versão no CJam.

{2P*mr_1dmrmC_++]p}ri*

Uma entrada de 1000 produz uma distribuição como:

insira a descrição da imagem aqui

Como funciona

O algoritmo é simplesmente x = 2 * Pi * rand(); print [x, x + 2 * acos(rand())]

{                 }ri*        e# Run this loop int(input) times
 2P*mr                        e# x := 2 * Pi * rand()
      _                       e# copy x
       1dmr                   e# y := rand()
           mC                 e# z := acos(y)
             _++              e# o := x + z + z
                ]             e# Wrap x and o in an array
                 p            e# Print the array to STDOUT on a new line

Atualização : 2 bytes salvos graças ao Martin!

Experimente aqui

Optimizer
fonte
1

Python 3, 144 117 bytes

(obrigado a Blckknght pela lambda ponteiro)

Usando o mesmo método que outros:

import math as m;from random import random as r;f=lambda n:[(x,x+2*m.acos(r()))for x in(2*m.pi*r()for _ in range(n))]

Na documentação do Python:

O Python usa o Mersenne Twister como gerador de núcleo. Produz flutuadores de precisão de 53 bits e possui um período de 2 19937 -1.

Resultado

>>> f(10)
[(4.8142617617843415, 0.3926824824852387), (3.713855302706769, 1.4014527571152318), (3.0705105305032188, 0.7693910749957577), (1.3583477245841715, 0.9120275474824304), (3.8977143863671646, 1.3309852045392736), (0.9047010644291349, 0.6884780437147916), (3.333698164797664, 1.116653229885653), (3.0027328050516493, 0.6695430795843016), (5.258167740541786, 1.1524381034989306), (4.86435124286598, 1.5676690324824722)]

E assim por diante.

Visualização

visualização

Zach Gates
fonte
Você pode economizar cerca de 20 bytes se você usar um lambda para a função e retornar uma compreensão da lista (com uma expressão gerador interno):f=lambda n:[(x,x+2*m.acos(r()))for x in(2*m.pi*r()for _ in range(n))]
Blckknght
Ah, eu tinha um lambda originalmente. Acho que não pensei em dobrar a compreensão da lista. Obrigado! @Blckknght
Zach Gates
Pode ser reduzido para 109 bytes, mexendo nas importações: tio.run/#python2
Triggernometry
1

Perl, 60

#!perl -l
use Math::Trig;print$x=2*pi*rand,$",$x+2*acos rand for 1..<>
nutki
fonte
1

R, 60 56 53 49 bytes

Um extra de 4 bytes graças a @JayCe e alterando-o para uma função.

Usando a mesma fórmula básica que as outras. R usa o método Mersenne-Twister por padrão, mas outros podem ser definidos. Produz uma lista separada por espaço.

function(n,y=acos(runif(n)))runif(n)*2*pi+c(y,-y)

Experimente online!

MickyT
fonte
Oi Micky, você pode salvar 4 bytes , tornando-o uma função e não definindo x.
Jayce
@JayCe que é muito melhor graças
MickyT
0

SmileBASIC, 62 bytes

INPUT N
FOR I=1TO N
X=PI()*2*RNDF()Y=ACOS(RNDF())?X+Y,X-Y
NEXT
12Me21
fonte