Costruisci un generatore di numeri casuali che superi i test di Diehard


50

Mentre qui ci sono molte domande sul code golf che riguardano la casualità, non ne ho ancora vista una che richieda effettivamente la creazione di un generatore di numeri pseudocasuali algoritmico. Ce n'è uno che ti chiede di generare un po 'di flusso, ma i test di casualità forniti su quello non erano molto rigorosi, e non è code-golf.

Il programma che scrivi avrà una singola funzione richiamabile che restituirà un numero intero casuale compreso tra 0 e 4294967295. Questa funzione non deve richiamare librerie o altre funzioni che non sono state anche scritte come parte del programma, in particolare chiamate a / dev / random o la libreria rand () integrata di una lingua. Più specificamente, si è limitati agli operatori di base della lingua in cui si sta lavorando, come le istruzioni aritmetiche, di accesso all'array e di controllo del flusso condizionale.

Il punteggio del tuo programma viene calcolato come segue:

Score = C / R

Dove C è la lunghezza del codice in caratteri e R è il numero di test di Diehard che il tuo generatore supera (se il tuo generatore di numeri casuali non supera almeno un test di Diehard, il suo punteggio è infinito ed è squalificato). Il tuo generatore supera un test Diehard se il file che genera fornisce un intervallo di valori P che sembrano essere distribuiti uniformemente lungo l'intervallo [0, 1).

Per calcolare R, utilizzare il generatore di numeri casuali con il seme predefinito per generare un file di dati binari da 16 MB. Ogni chiamata della funzione restituisce quattro byte; se la tua funzione è troppo lenta per restituire byte, questo comporterà un compromesso nel raggiungimento di un punteggio basso in base alla difficoltà di testare. Quindi, eseguilo attraverso i test di Diehard e controlla i valori P forniti. (Non cercare di implementarli da soli; usa quelli forniti qui )

Il punteggio più basso vince, ovviamente.


È consentito il codice che richiede la connettività Internet? (non accederà a nessuna funzione casuale online, ma forse ping o i valori di una chiamata api)
elssar

"Questa funzione non deve invocare alcuna libreria o altra funzione che non sia stata scritta come parte del programma." Ciò include le funzioni di connettività Internet. La tua generazione dovrebbe essere puramente algoritmica.
Joe Z.

La suite irriducibile prevede file di input di 10-11 MB.
primo

Il link ai test sembra essere rotto, ecco una possibile alternativa.
Campion 2012

Come devo fare questo per la mia risposta da difetto del cervello (rimossa di seguito)? Penso che il codice sia troppo lento per essere pratico
Christopher

Risposte:


6

Mathematica, 32/15 = 2.133

x=3;Mod[x=Mod[x^2,28!-67],2^32]&

Un'implementazione diretta di BBS .

File binario generato con:

f = %; (* assigns anonymous function declared in the previous expression to f *)
Export["random.bin", Array[f, 2^22], "UnsignedInteger32"];

Riepilogo dei risultati:

 1. BIRTHDAY SPACINGS TEST           .684805
 2. OVERLAPPING 5-PERMUTATION TEST   .757608/.455899
 3. BINARY RANK TEST                 .369264/.634256
 4. BINARY RANK TEST                 .838396
 5. THE BITSTREAM TEST                (no summary p-value)    
 6. OPSO, OQSO and DNA                (no summary p-value)
 7. COUNT-THE-1's TEST               .649382/.831761
 8. COUNT-THE-1's TEST                (no summary p-value)
 9. PARKING LOT TEST                 .266079
10. MINIMUM DISTANCE TEST            .493300
11. 3DSPHERES TEST                   .492809
12. SQEEZE                           .701241
13. OVERLAPPING SUMS test            .274531
14. RUNS test                        .074944/.396186/.825835/.742302
15. CRAPS TEST                       .403090/.403088/.277389

Pieno random.binqui.

File di registro completo qui.


28!-67è alquanto proibitivo. C'è un valore più piccolo che si adatterebbe in un numero intero a 64 bit?
primo

@primo Come Python, gli interi di Mathematica sono di precisione arbitraria per impostazione predefinita, quindi non causano problemi.
Campion 2012

Pensavo specificamente alla portabilità in C.
primo


21

Perl 28/13 ≈ 2.15

sub r{$s^=~($s^=$s/7215)<<8}

file di registro qui

Perl 29/13 ≈ 2.23

sub r{$s^=~($s^=$s<<8)/60757}

file di registro qui

Queste sono una specie di variazione su uno Xorshift , usando la divisione in virgola mobile anziché uno spostamento a destra. Entrambi superano 13 test su 15, fallendo solo i test 6 e 7.

Non sono esattamente sicuro di quanto sia lungo il ciclo, ma poiché il seguente codice non termina in un breve periodo di tempo, è probabilmente l'intero 2 32 :

$start = r();
$i++ while $start != r();
print $i;

Perl 39/10 = 3.9

$s=$^T;sub r{~($s=$s*$s%4294969373)||r}

Nota: se stai cercando un PRNG Blum-Blum-Shub-esque, la soluzione di Keith Randall è di gran lunga migliore di una di queste.

Come per la mia soluzione originale di seguito, anche questa è un'implementazione del Blum Blum Shub, con una grande differenza. I usa un modulo leggermente più grande di 2 32 ( M = 50971 • 84263 ) e ogni volta che si incontra un valore che non è un intero valido a 32 bit (ovvero maggiore di 2 32 ), restituisce il valore successivo nella rotazione invece. In sostanza, questi valori vengono eliminati, lasciando indisturbato il resto della rotazione, determinando una distribuzione quasi uniforme.

Sembra aver aiutato. Oltre a superare gli stessi 9 test di prima, ora supera anche in modo convincente il test della distanza minima. Un file di registro di esempio è disponibile qui .


Perl 33/9 ≈ 3.67 (Non valido?)

 $s=$^T;sub r{$s=$s*$s%4294951589}

Nota: questa soluzione potrebbe essere considerata non valida, poiché lo 0.00037% più alto dell'intervallo non verrà mai osservato.

Un'implementazione rapida e sporca di Blum Blum Shub . Dichiaro i seguenti risultati:

 1. passed - Birthday Spacings
 2. FAILED - Overlapping Permutations
 3. passed - Ranks of 31x31 and 32x32 Matrices
 4. passed - Ranks of 6x8 Matrices
 5. FAILED - Monkey Tests on 20-bit Words
 6. FAILED - Monkey Tests OPSO, OQSO, DNA
 7. FAILED - Count the 1s in a Stream of Bytes
 8. passed - Count the 1s for Specific Bytes
 9. passed - Parking Lot Test
10. FAILED - Minimum Distance Test
11. passed - Random Spheres Test
12. FAILED - The Squeeze Test
13. passed - Overlapping Sums Test
14. passed - Runs Test
15. passed - The Craps Test

Un file di registro di esempio può essere trovato qui , non esitate a contestare qualsiasi risultato. Il file per diehard può essere generato nel modo seguente:

print pack('N', r()) for 1..4194304

e quindi reindirizzare l'output in un file. La distanza minima sembra che potrebbe essere passata, ma se la esegui più volte è sempre molto vicina alla 1.0 , che indica un errore.


Dettagli

In generale, il Blum Blum Shub è un PRNG terribile, ma le sue prestazioni possono essere migliorate scegliendo un buon modulo. La M che ho scelto è 7027 • 611207 . Entrambi questi fattori primi, p e q , hanno residuo modulare 3 (mod 4) e gcd (φ (p-1), φ (q-1)) = 2 , che è il più basso possibile.

Sebbene questi siano gli unici criteri elencati nella pagina wiki, non sembra essere sufficiente. Quasi tutto il modulo che ho provato ha fallito ogni test. Ma c'è una manciata che supererà alcuni dei test e quello che ho scelto sembra essere eccezionalmente buono, per qualsiasi motivo.

Come nota finale, il Test 5 da solo sembra essere un buon indicatore di quanto sia buono il PRNG. Se non supera quasi il Test 5, fallirà in modo spettacolare il resto di essi.


BONUS: Perl 62/14 ≈ 4.43

$t=$^T;sub r{$t|=(($s=$s/2|$t%2<<31)^($t/=2))<<31for 1..37;$t}

Solo per i geek, questa è una versione a 32 bit del PRNG utilizzata nel Tetris originale per NES. Sorprendentemente, supera 14 dei 15 test!

 1. passed - Birthday Spacings
 2. passed - Overlapping Permutations
 3. passed - Ranks of 31x31 and 32x32 Matrices
 4. passed - Ranks for 6x8 Matrices
 5. passed - Monkey Tests on 20-bit Words
 6. passed - Monkey Tests OPSO, OQSO, DNA
 7. FAILED - Count the 1s in a Stream of Bytes
 8. passed - Count the 1s for Specific Bytes
 9. passed - Parking Lot Test
10. passed - Minimum Distance Test
11. passed - Random Spheres Test
12. passed - The Squeeze Test
13. passed - Overlapping Sums Test
14. passed - Runs Test
15. passed - The Craps Test

Il file di registro di esempio può prima qui .

Certo, il 1..37bit non è una trascrizione esatta. Nella versione originale, la routine entropica viene aggiornata 60 volte al secondo e quindi interrogata a intervalli casuali, in gran parte dipendente dall'input dell'utente. Per chiunque si preoccupi di smontare la ROM, inizia la routine dell'entropia 0xAB47.

Pseudo-codice in stile Python:

carry = entropy_1 & 1
entropy_1 >>= 1
entropy_2 = (entropy_2 >> 1) | (carry << 31)
carry = (entropy_1 & 1) ^ (entropy_2 & 1)
entropy_1 |= carry << 31

Sì, ho notato che il tuo algoritmo ha "fallito" il test bitstream, ma in realtà aveva alcuni valori al di sotto di 0.999999. Tuttavia, i tuoi test sembrano accurati.
Joe Z.

C'è un problema, tuttavia, e cioè che i numeri da 4294951589 a 4294967295 non hanno possibilità di verificarsi (anche se suppongo che sia parte del motivo per cui ha fallito alcuni dei test di Diehard).
Joe Z.

1
@JoeZeng sì, questo è un problema. È più evidente nel Test 5: la prima manche ha 151k parole mancanti e il resto solo 143k mancanti. Una soluzione sarebbe quella di scegliere un modulo leggermente più grande di 2 ^ 32 e consentire a valori troppo grandi di andare a zero, ma non sono riuscito a trovarne uno che funzioni bene. In tal caso, aggiornerò il post.
primo

7

Python, 46/15 = 3.0666

v=3
def R():global v;v=v**3%(2**32-5);return v

Utilizza esponenziazione modulare per generare casualità. 2 ** 32-5 è il numero primo più grande inferiore a 2 ^ 32. (Lo stesso problema con la mancata esecuzione del test n. 2).


Potresti incollare un file di registro?
primo


1
Windows stupido. Stava convertendo tutte le occorrenze di \re \nin \r\n, il che ovviamente distorce i risultati. Una correzione è scrivere il file direttamente usando f = open('file.bin', 'wb')e f.write.
primo

Questo nuovo punteggio è inferiore al punteggio precedente, quindi ora è la risposta accettata.
Joe Z.

Questo nuovo punteggio è stato nuovamente tagliato, quindi ho cambiato la risposta accettata.
Joe Z.

4

Rubino, 32/15 = 2.1333

Questa è la soluzione di Keith Randall, implementata in Ruby.

$v=3;def R;$v=$v**3%(2**32-5)end

@JoeZ Questa sembra essere la nuova risposta più bassa, legata alla nuovissima risposta Mathematica.
Riking

3

C # 144/15 = 9.6

uint a=15,b=26,y;uint q(int n){y=(a*1414549U+876619U)^(b*889453U+344753U);b=a;a=y>>12;return(a%256)<<n;}uint r(){return q(24)|q(16)|q(8)|q(0);}

Questo ha superato tutti i test.

Con non troppi caratteri in più passa TestU01.

Risultato: http://codepad.org/iny6usjV

    uint a = 15;
    uint b = 26;

    byte prng8()
    {
        uint y = ((a * 1414549U + 876619U) ^ (b * 889453U + 344753U)) >> 12;
        b = a;
        a = y;
        return (byte)y;
    }

    uint prng32()
    {
        return ((uint)prng8() << 24) | ((uint)prng8() << 16) | ((uint)prng8() << 8) | (uint)prng8();
    }

2

C # - 103/14 = 7.36

double j=999;uint N(){uint i=0,n=0;for(;i++<4;n=n*256+(uint)j%256)for(j/=277;j<100000;j*=j);return n;}

risultati

Supera tutto tranne il test n. 6
Vedi i risultati su http://codepad.org/k1NSoyQW

Spiegazione

C # non può competere con Ruby e Python per la terseness, come al solito, ma mi è piaciuto provare. Vi sono certamente altri valori che funzioneranno altrettanto bene (ovvero, il valore iniziale per j = 999 e il divisore = 277). Ho scelto questi dopo una breve sperimentazione.

Con wrapper per la creazione di file

class R
{
    public static void Main(string[] args)
    {
        var r = new R();
        using (var f = new System.IO.FileStream(".\\out.bin", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.Read))
        using (var b = new System.IO.BinaryWriter(f))
        {
            for (long i = 0; i < 12 * 1024 * 1024; i += 4)
            {

                b.Write(r.N());
            }
        }
    }

    double j = 999;

    uint N()
    {
        uint i = 0, n = 0;
        for (; i++ < 4; n = n * 256 + (uint)j % 256)
            for (j /= 277; j < 100000; j *= j) ;
        return n;
    }

}

1

Python, 41/15 = 2.73333

v=0
def R():global v;v=hash(`v`);return v

Un po 'barare usando la funzione hash integrata, ma è integrata, quindi non più cheat che usare altri builtin, come len. Il rovescio della medaglia, mi fa male dover pagare per la global v;dichiarazione ...

Supera tutti i test di Diehard (ho avuto un problema con il test n. 2, SEGV sulla mia macchina OSX. Per il mio punteggio, suppongo che passerà).

Ecco un driver per generare il file da 16 MB:

import sys
for i in xrange(1<<22):
  r=R()
  sys.stdout.write('%c%c%c%c'%(r&255, r>>8&255, r>>16&255, r>>24&255))

"Questa funzione non deve richiamare nessuna libreria o altra funzione che non sia stata scritta come parte del programma, in particolare chiamate a / dev / random o alla libreria rand () incorporata in una lingua." Mi dispiace, ma questo squalifica la tua iscrizione.
Joe Z.

Per essere chiari, "len" eliminerebbe anche la tua iscrizione.
Joe Z.

Dove disegni la linea? È +una funzione built-in, e quindi squalificato?
Keith Randall,

6
Ma in molte lingue, operatori e funzioni sono identici. Vedi +e __add__in python, o sovraccarico dell'operatore in c ++. So che mi sto spaccando i capelli, quindi considera questo esempio. In Python posso creare una mappa come questa {'a':5}:? Probabilmente dirai di sì, ma poi consideralo sotto le coperte, hash('a')viene chiamato quando lo fai.
Keith Randall,

2
Suppongo che traccerei la linea quando è necessario fare riferimento sintatticamente alla funzione in quel modo. Se riesci a trovare un hack in Python che ti permetterà di accedere direttamente all'indirizzo della mappa senza fare riferimento sintatticamente alla funzione "hash", potrei accettarlo.
Joe Z.

1

C, 38/15 = 2.533

long long x;f(){return(x+=x*x+9)>>32;}

Non sono riuscito a far funzionare i test Diehard sulla mia macchina, ma passa la suite PractRand fino a 8 GB di output, quindi suppongo che li supererebbe tutti.


0

Brain-Flak , 344 / (in sospeso)

<>((()()){})<> push the amount of iterations to do for the PRNG
(((((((((((((((((((((((((((((((((((()()()){}()){})){}{}){()()()()({}[()])}{})){}{})){}{})()){}{})()){}{})){}{})){}{}){}())){}{})){}{})()){}{})()){}{})){}{})){}{})()){}{})()){}{}) push M (one of the values for the Blum Blum Shub PRNG
((((((((((((()()()){}){}){})){}{}){()({}[()])}{}){}())){}{})()){}{}) push s see above
<>{({}[()])<>starts the loop
(({({})({}[()])}{}) squares the current number
(<>))<>{(({})){({}[()])<>}{}}{}<>([{}()]({}))mods by M
<>}{}<>loop ends

Provalo online!

Funziona bene, ma i collegamenti dei test irriducibili sono tutti rotti :( quindi fino a quando non ne otteniamo di nuovi non ho un punteggio finale

Questo utilizza il PRNG di Blum Blum Shub, quindi dovrebbe passare la maggior parte dei casi. I numeri utilizzati sono abbastanza grandi da non comparire schemi entro i 16 MB dei casi di test


se questo non è valido, dimmelo
Christopher,

1
Conto 344. Teorema: nessun programma Brak-flak completamente golfato ha un numero dispari di byte.
user202729

0

Obiettivo-C, 40/1 = 40

Approccio piuttosto intelligente, che sfrutta .hashqui per imbrogliare un po ', ma mi piace

for(int v=9;v=@(v).hash;printf("%i",v));
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.