Random Golf of the Day # 4: The Bertrand Paradox


19

Informazioni sulla serie

Prima di tutto, puoi trattarlo come qualsiasi altra sfida di golf del codice e rispondere senza preoccuparti della serie. Tuttavia, esiste una classifica in tutte le sfide. Puoi trovare la classifica insieme ad alcune ulteriori informazioni sulla serie nel primo post .

Anche se ho un sacco di idee in programma per la serie, le sfide future non sono ancora state messe sulla pietra. Se hai qualche suggerimento, per favore fatemelo sapere sul post sandbox pertinente .

Hole 4: The Bertrand Paradox

Il paradosso di Bertrand è un problema interessante, che mostra come metodi diversi per scegliere accordi casuali in un cerchio possano produrre diverse distribuzioni di accordi, i loro punti medi e le loro lunghezze.

In questa sfida dovresti generare accordi casuali del cerchio unitario, usando il metodo "giusto", cioè uno che produce una distribuzione di accordi invariante in scala e traduzione. Nell'articolo di Wikipedia collegato, "Metodo 2" è un tale metodo.

Ecco le regole esatte:

  • Dovresti prendere un numero intero positivoN che specifica quanti accordi dovrebbero essere restituiti. L'output dovrebbe essere un elenco di Naccordi, ognuno specificato come due punti sul cerchio unitario, dato dal loro angolo polare in radianti.
  • Il tuo codice dovrebbe essere in grado di restituire almeno 2 20 valori diversi per ciascuno dei due angoli . Se il tuo RNG disponibile ha un intervallo più piccolo, devi prima costruire un RNG con un intervallo sufficientemente ampio sopra quello incorporato oppure devi implementare il tuo RNG adatto . Questa pagina può essere utile per questo.
  • La distribuzione degli accordi deve essere indistinguibile da quella prodotta dal "Metodo 2" nell'articolo Wikipedia collegato. Se si implementa un algoritmo diverso per scegliere gli accordi, si prega di includere una prova di correttezza. Qualunque algoritmo si scelga di implementare, deve teoricamente essere in grado di generare qualsiasi accordo valido nel cerchio unitario (salvo le limitazioni del PRNG sottostante o tipi di dati a precisione limitata).
  • L'implementazione dovrebbe utilizzare e restituire numeri in virgola mobile (almeno 32 bit di larghezza) o numeri in virgola fissa (almeno 24 bit di larghezza) e tutte le operazioni aritmetiche devono essere accurate entro un massimo di 16 ulp .

È possibile scrivere un programma completo o una funzione e accettare input tramite STDIN (o alternativa più vicina), argomento della riga di comando o argomento della funzione e produrre output tramite STDOUT (o alternativa più vicina), valore di ritorno della funzione o parametro di funzione (out).

L'output può essere in qualsiasi elenco o formato stringa conveniente, purché i singoli numeri siano chiaramente distinguibili e il loro numero totale sia sempre uniforme.

Questo è il golf del codice, quindi vince l'invio più breve (in byte). E, naturalmente, la presentazione più breve per utente entrerà anche nella classifica generale della serie.

visualizzazione

È possibile utilizzare il frammento seguente per eseguire il rendering delle linee generate e controllarne la distribuzione. Basta incollare un elenco di coppie di angoli nell'area di testo. Lo snippet dovrebbe essere in grado di gestire quasi tutti i formati di elenco, purché i numeri siano semplici numeri decimali (nessuna notazione scientifica). Ti consiglio di usare almeno 1000 righe per avere una buona idea della distribuzione. Ho anche fornito alcuni dati di esempio per i diversi metodi presentati nell'articolo di seguito.

Dati di esempio generati con il metodo 1.

Dati di esempio generati con il metodo 2.

Dati di esempio generati con il metodo 3.

Classifica

Il primo post della serie genera una classifica.

Per assicurarti che le tue risposte vengano visualizzate, inizia ogni risposta con un titolo, utilizzando il seguente modello Markdown:

# Language Name, N bytes

dov'è Nla dimensione del tuo invio. Se si migliora il punteggio, è possibile mantenere i vecchi punteggi nel titolo, colpendoli. Per esempio:

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

(La lingua non è attualmente visualizzata, ma lo snippet richiede e analizza, e in futuro potrei aggiungere una classifica linguistica).

Risposte:


12

Codice macchina IA-32, 54 byte

Hexdump del codice:

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

Utilizza un algoritmo (leggermente modificato) descritto da Wikipedia. In pseudo-codice:

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

Uso l'intervallo -1...1perché è facile creare numeri casuali in questo intervallo: l' rdrandistruzione genera un numero intero tra-2^31 e 2^31-1, che può essere facilmente diviso per 2 ^ 31.

Avrei dovuto usare la gamma 0...1 per l'altro numero casuale (x), che viene inserito acos; tuttavia, la parte negativa è simmetrica con la parte positiva: i numeri negativi producono accordi la cui apertura è maggiore di pi radianti, ma allo scopo di illustrare il paradosso di Bertrand, non importa.

Poiché il set di istruzioni 80386 (o x87) non ha acosun'istruzione dedicata , ho dovuto esprimere il calcolo usando solo l' atanistruzione:

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

Ecco il codice sorgente che genera il codice macchina sopra:

__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
    }
}

Per generare due numeri casuali, il codice utilizza un ciclo nidificato. Per organizzare il loop, il codice sfrutta il ecxpositivo del registro (contatore del loop esterno). Nega temporaneamente ecxe quindi lo fa di nuovo per ripristinare il valore originale. L' jsistruzione ripete il ciclo quando ecxè negativo (ciò accade esattamente una volta).


Mi piace questa risposta per l'utilizzo dell'assembly IA32! Solo per dire: questo non è un codice macchina strettamente 386 poiché 80386 ovviamente non ha istruzioni di rdrand né necessario un coprocessore FP
user5572685

Sì, IA32 è un nome migliore. Non abbastanza specifico ma probabilmente più corretto di 80386.
Anatolyg

7

Pyth, 25 23 22 byte

Una porta della risposta C ++ 11 di rcrmn. Questo è il mio primo utilizzo di Pyth e mi sono divertito molto!

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

Versione a 23 byte:

VQJ*y.n0O0K.tOZ4+JK-JKd

Taglia un byte cambiando il programma per usare folds + somme e impostando J su una tupla, rimuovendo K.

Originale:

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

Taglia 2 byte grazie a @orlp.

Spiegazione:

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

1
Suggerimenti Pyth: *2_è lo stesso di y_. La variabile Zinizialmente è 0, quindi puoi rimuovere lo spazio .tO0 4scrivendo .tOZ4.
orlp,

1
Sto contando 25 byte ...
Maltysen,

Inoltre, puoi formattare meglio l'output con,+JK-JK
Maltysen,

@Maltysen Entrambi riparati. Grazie!
kirbyfan64sos,

@orlp Non avevo idea ye mi sono dimenticato Z. Fisso; Grazie!
kirbyfan64sos,

6

Julia, 48 byte

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

Questo utilizza l'algoritmo del metodo 2, come la maggior parte delle risposte finora. Crea una funzione lambda che accetta un input intero e restituisce un array nx 2. Per chiamarlo, dagli un nome, ad es f=n->....

Ungolfed + spiegazione:

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

Mi piace molto l'aspetto delle visualizzazioni, quindi ne includerò una. È il risultato di f(1000).

Circle


5

Pyth, 22 byte

Una porta della risposta C ++. Avevo un'altra soluzione da 23 byte (Now 22!), Ma era quasi una copia della risposta pyth di @ kirbyfan64sos con ottimizzazioni, quindi ho dovuto pensare un po 'fuori dagli schemi e usare creativamente (ab) l'operatore fold.

m,-Fdsdm,y*.nZOZ.tOZ4Q

Si noti che questo non funziona in questo momento a causa di un bug nell'operatore di piegatura dopo l'introduzione di reduce2. Sto inserendo una richiesta pull.

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

Per riferimento questa era la mia altra soluzione che funziona allo stesso modo: VQKy*.nZOZJ.tOZ4,+KJ-KJ


Hai scritto male il mio nome utente ... :(
kirbyfan64sos

@ kirbyfan64sos derp. Siamo spiacenti;)
Maltysen,

4

IDL, 65 byte

Ovviamente questo è lo stesso algoritmo di @rcrmn, anche se l'ho derivato in modo indipendente prima di leggere la loro risposta.

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

La funzione randomu di IDL utilizza Mersenne Twister, che ha un periodo di 2 19937 -1.

EDIT: ho eseguito 1000 accordi attraverso il visualizzatore sopra, ecco uno screenshot del risultato:

IDL bertrand


4

C ++ 11, 214 byte

#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<<';';}}

Quindi questa è un'implementazione diretta dell'algoritmo giusto dalla pagina di Wikipedia. Il problema principale qui nel golf è i nomi così spaventosi che hanno le classi di generatori casuali. Ma, a differenza del buon vecchio, è almeno adeguatamente uniforme.

Spiegazione:

#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
    }
}

1
Questo fattore M_PI_2sembra sospetto. Penso che dovrebbe essere 1 invece.
Anatolyg

Sì, assolutamente giusto, risolverlo ora! Molte grazie!
rorlork,

4

APL, 46 byte

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

Il mio primo programma APL di sempre! Sicuramente può essere notevolmente migliorato (poiché manca la mia comprensione generale di APL), quindi qualsiasi suggerimento sarebbe fantastico. Questo crea una funzione fche accetta un intero come input, calcola le coppie di punti di accordo usando il metodo 2 e stampa ogni coppia separata da una nuova riga.

Puoi provarlo online !

Spiegazione:

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: la mia precedente soluzione a 19 byte non era valida poiché restituiva (x, y) anziché (x + y, xy). La tristezza abbonda.


3

Java, 114 byte

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

Implementazione di base in Java. Utilizzare come espressione lambda.

Esempio di utilizzo


Non puoi ridurre le dimensioni memorizzando da Mathqualche parte? O qualcosa? (Non sono un programmatore Java)
Ismael Miguel,

@IsmaelMiguel Costerebbe 2 caratteri in più.
TheNumberOne,

Ci dispiace: / È allettante provare a ridurre il numero di volte che Mathmostra. Cosa dice il meta sull'utilizzo di un codice per generare un altro codice per risolvere il problema?
Ismael Miguel,

2
@IsmaelMiguel Questo è un gioco equo, anche se rimarrò sorpreso se in realtà sei meglio nel metagolfing che nel golf.
Martin Ender,

3

Rubino, 72 byte

Il mio primo golf qui! Ho usato lo stesso codice di tutti, spero che vada bene

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

2

Java, 115 123

Questo è fondamentalmente lo stesso della maggior parte degli altri, ma ho bisogno di un punteggio Java per questo buco, quindi eccolo qui:

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();}

È possibile trovare 1000 accordi di esempio su pastebin , ecco i primi cinque di una corsa:

8.147304676211474 3.772704020731153
8.201346559916786 3.4066194978900106
4.655131524088468 2.887965593766409
4.710707820868578 3.8493686706403984
3.3839198612642423 1.1604092552846672

1

CJam, 24 22 byte

Simile ad altri algoritmi, ecco una versione in CJam.

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

Un input di 1000 produce una distribuzione come:

inserisci qui la descrizione dell'immagine

Come funziona

L'algoritmo è semplicemente 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

Aggiornamento : 2 byte salvati grazie a Martin!

Provalo qui


1

Python 3, 144 117 byte

(grazie a Blckknght per il lambdapuntatore)

Utilizzando lo stesso metodo di altri:

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))]

Dalla documentazione di Python:

Python utilizza Mersenne Twister come generatore principale. Produce float di precisione a 53 bit e ha un periodo di 2 19937 -1.

Produzione

>>> 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 così via.

visualizzazione

visualizzazione


È possibile salvare circa 20 byte se si utilizza un lambda per la funzione e si restituisce una comprensione dell'elenco (con un'espressione del generatore interno):f=lambda n:[(x,x+2*m.acos(r()))for x in(2*m.pi*r()for _ in range(n))]
Blckknght

Ah, all'inizio avevo una lambda. Immagino di non aver pensato di raddoppiare la comprensione delle liste. Grazie! @Blckknght
Zach Gates

Può essere ridotto a 109 byte per giocherellare con le importazioni: tio.run/#python2
Triggernometry

1

Perl, 60

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

1

R, 60 56 53 49 byte

Altri 4 byte grazie a @JayCe e cambiandolo in una funzione.

Utilizzando la stessa formula di base delle altre. R utilizza il metodo Mersenne-Twister per impostazione predefinita, ma altri possono essere impostati. Emette un elenco separato da spazi.

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

Provalo online!


Ciao Micky, puoi salvare 4 byte rendendolo una funzione e non definendo x.
JayCe

@JayCe grazie molto meglio
MickyT

0

SmileBASIC, 62 byte

INPUT N
FOR I=1TO N
X=PI()*2*RNDF()Y=ACOS(RNDF())?X+Y,X-Y
NEXT
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.