Genera un flusso di bit pseudocasuale (completamente deterministico)


11

Ispirato da Random con le mani legate :


L'obiettivo. il gol

L'obiettivo di questa sfida è scrivere un programma che generi un flusso di bit pseudocasuale, che è una stringa di 1 e 0 che sembra essere puramente casuale, ma che in realtà è generato in modo deterministico. Il tuo programma dovrebbe generare una stringa di 1 e 0 (con spazi bianchi opzionali) e dovrebbe soddisfare i seguenti requisiti:

  1. Con tempo e memoria illimitati, il programma deve continuare a emettere una stringa di 1 e 0 per sempre
  2. Il tuo programma deve emettere più di 1000 bit casuali in circa un minuto, su una macchina ragionevole. Se questo requisito è impossibile, lo diminuirò.
  3. La stringa di bit può essere ripetuta, ma la lunghezza della sezione ripetuta deve essere superiore a 1000 bit.
  4. La stringa di bit deve superare il maggior numero possibile di test di casualità (descritti di seguito).
  5. Il programma non deve ricevere alcun input da alcuna fonte esterna o utilizzare alcuna funzione simile a rand ().
  6. A causa dei requisiti di cui sopra, il programma deve generare la stessa stringa esatta di bit ogni volta che viene eseguito.

Test di casualità n. 1

La stringa di bit pseudocasuali non deve includere alcun modello evidente durante l'ispezione visiva.

Test di casualità n. 2 (soggetto a modifiche in base ai commenti)

La stringa di bit deve contenere una distribuzione uguale di 1 e 0 secondi. Per testare questo (e anche altre cose), il flusso di bit è suddiviso in segmenti lunghi 3 bit, come ad esempio 101|111|001.

Di tutti questi segmenti, 1/8 di essi dovrebbero avere tre 1 e nessun 0, 3/8 di essi dovrebbero avere due 1 e uno 0, 3/8 di loro dovrebbero avere 1 e due 0 e 1/8 di loro non dovrebbero avere 1 e tre 0.

Test di casualità n. 3

Una "corsa" è definita come una serie consecutiva di bit che hanno tutti lo stesso valore. La stringa 1001001110ha tre serie di dimensioni 1 ( 1..1.....0), due serie di dimensioni 2 ( .00.00....) e una serie di dimensioni 3 ( ......111.). Si noti che le esecuzioni non si sovrappongono.

Di una stringa di 1000 bit casuali, dovrebbero esserci circa 250 serie di dimensioni 1, 125 serie di dimensioni 2, 62 serie di dimensioni 3, ecc. In generale, per dimensioni di serie R, dovrebbero esserci 1000/(2**(R+1))righe di tali dimensioni.

Test di casualità n. 4

I primi 840 bit sono divisi in due metà di 420 bit ciascuno. Ogni bit nella prima metà viene confrontato con il bit corrispondente nella seconda metà. I due bit dovrebbero corrispondere al 50% circa delle volte.


Ecco il codice sorgente di un programma Perl che esegue i test da 2 a 4. A partire da ora, richiede che la stringa di bit non contenga spazi bianchi.


Tempo di criterio vincente obiettivo!

Il vincitore è il programma che supera tutti e 6 i requisiti e tutti i test di casualità nella misura in cui è indistinguibile dalla casualità. Se più programmi compiono questo, allora vincerà quello che impiega più tempo per ripetersi. Se più programmi compiono questo, allora potrei dover trovare altri test di casualità per agire come tie-breaker.


# 2 e # 3 non sono davvero ottimi criteri per la casualità. Soprattutto per il numero 2, un campione casuale probabilmente non presenterà questa caratteristica. Forse puoi fare un campione più grande? Suggerirei qualcosa tra il 100 e il 300.
Joel Cornett,

Un metodo di misurazione migliore sarebbe una media mobile, poiché la media su una grande finestra sul flusso di bit non cambierà molto (e dovrebbe essere intorno allo 0,5)
Joel Cornett

@JoelCornett Grazie per il consiglio. Non so molto sui test di casualità. Cambierò # 2 in qualcos'altro e sto leggendo delle medie mobili.
PhiNotPi

1
Nessun problema. Le sequenze casuali tendono a raggrupparsi e non essere distribuite uniformemente, questo è un fatto che a volte viene utilizzato nella contabilità per rilevare le frodi. (I numeri fraudolenti saranno spesso distribuiti in modo troppo uniforme, perché le persone che li inventano confondono l'uniformità per casualità)
Joel Cornett,

Posso usare le funzioni crittografiche integrate (come AES o SHA-2)?
CodesInCos

Risposte:


8

C, 61

main(s,n){for(n=1u<<31;putchar((s%=n)/(n/2)&1|48);s*=65539);}

Sì, lo so che non è il golf di codice. Questo è ovviamente piuttosto un anti-soluzione ... ma sicuramente soddisfa i tuoi criteri.

fuori | testa -c840
$ ./a.out | testa -c840 | perl tester.pl
Test 2: 1 (1) 2.9333333333333333 (3) 3.1 (3) 0.966666666666667 (1)
Test 3: 214 99 71 24 7 5 1 1 2 2
Test 4: 0.495238095238095

Il periodo è di 2²⁹.


6
Questo dimostra quanto sia difficile distinguere la casualità da qualcosa che è ampiamente noto per essere uno dei peggiori generatori di numeri casuali esistenti. +1.
PhiNotPi

8

Mathematica 78 53 caratteri

Le cifre della rappresentazione binaria di Pi sembrano comportarsi come se fossero prodotte caoticamente sebbene ciò non sia dimostrato.

La seguente semplice routine restituisce in modo deterministico come stringa le cifre binarie di pi, corrispondenti alle dcifre decimali:

f[d_]:=ToString@FromDigits@RealDigits[N[Pi,d],2][[1]]

uso

Se richiediamo la controparte di 301 cifre decimali di Pi, riceviamo 1000 cifre binarie.

f[301]
StringLength[%]

(* out *)
1100100100001111110110101010001000100001011010001100001000110100110001001100011001100010100010111000000011011100000111001101000100101001000000100100111000001000100010100110011111001100011101000000001000001011101111101010011000111011000100111001101100100010010100010100101000001000011110011000111000110100000001001101110111101111100101010001100110110011110011010011101001000011000110110011000000101011000010100110110111110010010111110001010000110111010011111110000100110101011011010110110101010001110000100100010111100100100001011011010101110110011000100101111001111110110001101111010001001100010000101110100110100110001101111110110101101011000010111111111101011100101101101111010000000110101101111110110111101110001110000110101111111011010110101000100110011111101001011010111010011111001001000001000101111100010010110001111111100110010010010010100001100110010100011110110011100100010110110011110111000010000000000111110010111000101000010110001110111111000001011001100011011010010010000011011000011100011

1000 (* characters *)

Poiché Pi è un numero irrazionale, non esiste un punto. Tuttavia, ci saranno vincoli pratici dovuti all'hardware in esecuzione.

Test 1 Mi sembra buono.

Test 2

d=301;
Partition[RealDigits[N[Pi,d],2][[1]],{3}];
Tally[%]
(* out *)
{{{1,1,0},35},{{0,1,0},45},{{0,0,0},41},{{1,1,1},40},
{{0,1,1},50},{{1,0,1},32},{{1,0,0},43},{{0,0,1},47}}

Controllo più approfondito:

d=10^6;
Partition[RealDigits[N[Pi,d],2][[1]],{3}];
Tally[%]

{{{1,1,0},138565},{{0,1,0},138146},{{0,0,0},138260},{{1,1,1},138427},
{{0,1,1},139119}, {{1,0,1},138404},{{1,0,0},137926},{{0,0,1},138462}}

Test 3: esecuzioni

d=10^6;
res3=SortBy[Tally@Split@RealDigits[N[Pi,d],2][[1]],Last]/.{a_,b_}:> {Length[a],b}
ListPlot[res3 ,AxesLabel-> {"Run Length","Runs"},AxesOrigin->{0,0}]

Ho eseguito un gran numero di casi per verificare sistematicamente la distribuzione delle corse. In circa 3 milioni di cifre binarie, c'erano 830k corse di 1, 416k corse di 2, 208k corse di 3, 104k corse di 4, ecc.

corre 2 Test 4: corrispondenza della prima e della seconda metà dei dati

Le partite sono i 212 casi di 0 e 2; i disallineamenti sono i 208 casi in cui la somma delle rispettive cifre è 1.

d=301;
Tally[Plus@@Partition[Take[RealDigits[N[Pi,d],2][[1]],840],420]]

(* out *)
{{1,208},{0,108},{2,104}}

sincronizzazione

Sono necessari meno di due secondi per calcolare 3321928 cifre binarie (corrispondenti a 10 ^ 6 cifre decimali).

(r=f[10^6]);//AbsoluteTiming
StringLength[r]

(*out*)
{1.785928,Null}    
3321928

1
Sapevo che qualcuno avrebbe fatto questo ...
cessò di girare in senso antiorario il

1
Frutto basso, giusto?
DavidC

Non potresti usare einvece di pisalvare un byte?
pepery

È edistribuito caoticamente?
DavidC,

3

Python, 90

g=[19]
print(''.join("01"[(g.append((11*g[-1]+13)%1024)or g[-1])>512]for i in range(1000)))

gè il valore del seme. Il campionamento casuale presenta una distribuzione straordinariamente normale ripetuta campionamento casuale dei mezzi di campionamento ha prodotto una media 0.506e una deviazione standard di .0473(dimensione del campione di 1000). Sfortunatamente, la casualità è altamente sensibile al seme iniziale. Il seme nel codice sopra mi ha dato la migliore casualità: p

AGGIORNARE

Vediamo come questo codice regge i test del PO:

Test n. 1

Questo è un po 'soggettivo ... ma mi sembra piuttosto irregolare.

Test n. 2

Tre 1: 0,141
Due 1: 0,371
Uno 1: 0,353
Zero 1: 0,135

Test n. 3

Funziona per dimensione:

8: 11
7: 3
6: 7
5: 13
4: 32
3: 67
2: 119
1: 216

Test n. 4

Rapporto di uguaglianza: 0,94 Questo è un refuso. Si aggiornerà presto con il numero corretto.


1
È possibile rimuovere gli spazi bianchi prima di 'for'.
daniero,

2

Haskell 74 58

main=print$iterate(read.take 9.show.(^3))7>>=show.(`mod`2)

Grazie a Shiona per la semplificazione. risultati:

/ pseudorandom | head -c 1000

./pseudorandom | head -c 1000 | perl test.pl

Test 2: 0.966666666666667 (1) 2.4 (3) 3.3 (3) 1.33333333333333 (1)

Test 3: 260 108 66 33 15 11 5 2

Test 4: 0.495238095238095

Questo è anche un terribile generatore pseudo-casuale (simile a quello usato da von-Neuman). Per quelli che non erano a conoscenza concatMap == (=<<) == flip . (>>=)(per gli elenchi)


È possibile sostituire \x->if odd x then"1"else"0"con show.(`mod`2).
shiona,

1

La domanda equivale essenzialmente a "implementare un codice di flusso". Quindi implemento RC4, poiché è relativamente semplice.

Non uso alcuna chiave e rilascio i primi 100000 bit, perché l'inizio di RC4 è un po 'distorto, soprattutto da quando ho saltato la pianificazione delle chiavi. Ma mi aspetto che passi il test anche senza quello (risparmiando 20 caratteri di codice).

Normalmente si produrrebbe un intero byte per ciclo, ma la conversione in binario è piuttosto brutta in C #, quindi semplicemente scartare tutto tranne il bit meno significativo.

var s=Enumerable.Range(0,256).ToArray();
byte i=0,j=0;
for(int k=0;;k++)
{
    i++;
    j+=(byte)s[i];
    var t=s[i];s[i]=s[j];s[j]=t;
    if(k>99999)
        Console.Write(s[i]+s[j]&1);
}

O senza spazi:

var s=Enumerable.Range(0,256).ToArray();byte i=0,j=0;for(int k=0;;k++){i++;j+=(byte)s[i];var t=s[i];s[i]=s[j];s[j]=t;if(k>99999)Console.Write(s[i]+s[j]&1);}

C #, 156 caratteri, funziona in modalità istruzione di LinqPad. Per un programma C # completo aggiungere la solita piastra della caldaia.


Potremmo anche usare cripto primitive integrate (soluzione di Cheater):

var h=SHA256.Create();for(BigInteger i=0;;i++){Console.Write(h.ComputeHash(i.ToByteArray())[0]%2);}

(C #, 99 caratteri, funziona in modalità istruzione di LinqPad. Per il normale compilatore C # è necessario aggiungere un po 'di boilerplate)

L'output delle funzioni di hash crittografico è progettato per essere indistinguibile dai dati casuali, quindi mi aspetto che passi tutti i test di casualità (muori di più, ...) che ci passi, ma sono troppo pigro per testarlo.


1

C, 52 caratteri

main(a){for(a=1;putchar(48+a%2);a=a/2^-(a%2)&576);}

Questo è un LFSR a 10 bit, risultati del test:

$ ./a.out |head -c 1000 | perl randtest.pl
Test 2: 1.13333333333333 (1) 2.86666666666667 (3) 3.16666666666667 (3) 0.833333333333333 (1)
Test 3:  251 122 64 32 16 8 4 2  1
Test 4: 0.466666666666667

adovrebbe iniziare come 1, (supponendo che sia chiamato senza argomenti). Inoltre potresti rimanere a=nel mezzo, qualcosa del genere a=a/2^-!putchar(49-a%2)%576(prendendoti alcune libertà con l'algoritmo)
walpen

@walpen: La mia implementazione iniziale non è stata impostata a, l'ho cambiata a causa di " The program must not take any input from any external sources"
Hasturkun

1

Sage / Python

Questo programma stampa le cifre binarie più a destra comuni a ogni torre di esponenziale sufficientemente alta di forma 3 3 3 3 . . . Per quanto ciò possa mai essere generato in modo fattibile, queste sono le cifre binarie più a destradel numerodiGraham. La sequenza delle cifre è infinita e non periodica.

m = 1; x = 3; last = 0
while True:
    m *= 2; x = pow(3,x,m); l = len(bin(x))
    print '1' if l > last else '0',
    last = l

Per 1000 cifre, ci sono voluti meno di 2 secondi; tuttavia, il tempo aumenterà molto più velocemente che linearmente nel numero di cifre.

I risultati del test con il programma OP sono

Test 2: 1.26666666666667 (1) 3.16666666666667 (3) 2.8 (3) 0.766666666666667 (1)
Test 3:  268 126 61 30 20 7 2  1 1
Test 4: 0.466666666666667

(Vedi Le cifre più a destra di G sono casuali? Per più di 32000 cifre e ulteriori test statistici.)


1

Giava, 371 317

Basato su un LFSR a 128 bit (i tocchi di bit provengono dalla nota 52 dell'app xilinx )

EDIT: Non ero soddisfatto dell'uso di BigInteger, quindi questa versione no. Hai salvato alcuni personaggi. L'output potrebbe essere un po 'meno casuale perché non riuscivo a pensare a un buon metodo di "seeding".

Nuovo codice: argomenti: BITS_TO_PRINT

class R{public static void main(String[]a){int L=65536;int[]v={0,128,126,101,99};int[]b=new int[L];for(int x=0;x<L;x++)b[x]=(x*x)&1;for(int i=0;i<Integer.parseInt(a[0])+L;i++){if(1!=(b[v[1]]^b[v[2]]^b[v[3]]^b[v[4]]))b[v[0]]=1;else b[v[0]]=0;if(i>L)System.out.print(b[v[0]]);for(int j=0;j<5;j++)v[j]=(v[j]-1)&(L-1);}}}

Vecchia versione: Argomenti: SEED, BITS_TO_PRINT

import java.math.BigInteger;class R{public static void main(String[]a){BigInteger v=new BigInteger(a[0]);BigInteger m=new BigInteger("ffffffffffffffffffffffffffffffff",16);for(int i=Integer.parseInt(a[1]);i>0;i--){v=v.shiftLeft(1);if(!(v.testBit(128)^v.testBit(126)^v.testBit(101)^v.testBit(99))){v=v.setBit(0);}v=v.and(m);java.lang.System.out.print(v.testBit(0)?1:0);}}}

Nuova versione: esempio di output, bit = 100:

011001100111000110010100100111011100100111000111001111110110001001100000100111111010111001100100011

1
A proposito, presumo che entrambi gli account Noah di questo post siano la stessa persona. Se è così, puoi chiedere a un moderatore di unirli su meta.codegolf.stackexchange.com
Peter Taylor,

0

JavaScript: da 1ms a 2ms per 1000 bit pseudo-casuali (da 139ms a 153ms per 100000 bit)

Questa soluzione utilizza il fatto che le radici quadrate sono irrazionali e quindi praticamente casuali. Fondamentalmente, prende la radice quadrata di 2 per iniziare, la converte in binaria, butta via la parte iniziale che corrisponde alla radice precedente, la aggiunge alla stringa casuale, si ripete con il successivo numero più alto (o torna a 2 se il numero ripetuto ed era lungo almeno 30 bit) e restituisce la stringa casuale una volta che è abbastanza lunga.

var getDeterministicPseudoRandString = function(length){
    var randString = '';

    var i = 2;
    var prevRand = '';

    outerLoop:
    while(randString.length < length){
        var nextRand, nextFullRand = Math.sqrt(i++).toString(2).substring(1).replace('.', '');
        nextRand = nextFullRand;
        for(var j = prevRand.length; j > 0; j--){
            var replaceString = prevRand.substring(0, j);

            nextRand = nextFullRand;

            if(nextFullRand.indexOf(replaceString) == 0){
                if(j == prevRand.length && j > 30){
                    //start i over at 2
                    console.log('max i reached: ' + i);

                    i = 2;
                    continue outerLoop;
                } else {
                    nextRand = nextFullRand.replace(replaceString, '');
                }

                break;
            }
        }
        prevRand = nextFullRand;

        randString += nextRand;
    }

    return randString.substring(0, length);//Return the substring with the appropriate length
};

Non ho ancora superato i test, ma immagino che andrà bene con loro. Ecco un violino in modo da poterlo vedere in azione. Per i miei tempi, ho appena eseguito il programma più volte e ho preso i valori più veloci e più lenti come intervalli.


0

Pitone

import hashlib
x=''
while 1:
    h=hashlib.sha512()
    h.update(x)
    x=h.digest()
    print ord(x[0])%2

Dovrebbe avere un periodo di circa 2 ^ 512.


0

perl, 44 byte

So che questo non è codice golf, ma sono sempre stato un fan di prendere i bit di basso ordine di una semplice funzione quadratica, ad esempio:

$x=1/7;print substr($x*=4-4*$x,9,1)%2while 1

Il periodo è più lungo di 3 miliardi, ma ho esaurito lo spazio su disco per calcolare di più.


1
puoi salvare 3 caratteri accostando costanti numeriche e parole chiave e distribuendo anche quello 4:$x=1/7;print substr($x*=4-4*$x,9,1)%2while 1
ardnew
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.