Gere um fluxo de bits pseudo-aleatório (completamente determinístico)

11

Inspirado pela Random com as mãos atadas :


O objetivo

O objetivo desse desafio é escrever um programa que gere um fluxo de bits pseudo-aleatório, que é uma sequência de 1s e 0s que parece ser puramente aleatória, mas que na verdade é gerada de maneira determinística. Seu programa deve gerar uma sequência de 1 e 0s (com espaço em branco opcional) e deve passar os seguintes requisitos:

  1. Com tempo e memória ilimitados, seu programa deve continuar produzindo uma sequência de 1s e 0s para sempre
  2. Seu programa deve gerar mais de 1000 bits aleatórios em aproximadamente um minuto, em uma máquina razoável. Se esse requisito for impossível, eu o diminuirei.
  3. A sequência de bits pode se repetir, mas o comprimento da seção de repetição deve ser superior a 1000 bits.
  4. A sequência de bits deve passar no maior número possível de testes de aleatoriedade (descritos abaixo).
  5. O programa não deve receber nenhuma entrada de fontes externas ou usar qualquer função rand () integrada.
  6. Devido ao requisito acima, o programa deve gerar a mesma seqüência exata de bits cada vez que é executado.

Teste de aleatoriedade # 1

A sequência de bits pseudo-aleatórios não deve incluir nenhum padrão óbvio na inspeção visual.

Teste de aleatoriedade # 2 (sujeito a alterações com base nos comentários)

A cadeia de bits deve conter uma distribuição igual de 1s e 0s. Para testar isso (e outras coisas também), o fluxo de bits é dividido em segmentos com 3 bits, como 101|111|001.

De todos esses segmentos, 1/8 deles deve ter três 1s e nenhum 0s, 3/8 deles devem ter dois 1s e um 0, 3/8 deles devem ter um 1 e dois 0s e 1/8 deles não devem ter 1s e três 0s.

Teste de aleatoriedade # 3

Uma "execução" é definida como uma série consecutiva de bits que todos têm o mesmo valor. A cadeia 1001001110possui três execuções do tamanho 1 ( 1..1.....0), duas execuções do tamanho 2 ( .00.00....) e uma execução do tamanho 3 ( ......111.). Observe que as execuções não se sobrepõem.

De uma sequência de 1000 bits aleatórios, deve haver cerca de 250 execuções do tamanho 1, 125 execuções do tamanho 2, 62 execuções do tamanho 3, etc. Em geral, para o tamanho da execução R, deve haver aproximadamente 1000/(2**(R+1))execuções desse tamanho.

Teste de aleatoriedade # 4

Os primeiros 840 bits são divididos em duas metades de 420 bits cada. Cada bit na primeira metade é comparado ao bit correspondente na segunda metade. Os dois bits devem corresponder cerca de cinquenta por cento do tempo.


Aqui está o código fonte de um programa Perl que executa os testes 2 a 4. A partir de agora, ele exige que a sequência de bits não contenha nenhum espaço em branco.


Tempo de critério de vencimento objetivo!

O vencedor é o programa que passa todos os 6 requisitos e todos os testes de aleatoriedade na medida em que é indistinguível da aleatoriedade. Se vários programas conseguirem isso, o que levar mais tempo para repetir vencerá. Se vários programas conseguirem isso, talvez seja necessário encontrar mais testes de aleatoriedade para atuar como desempate.

PhiNotPi
fonte
Os números 2 e 3 não são realmente muito bons critérios de aleatoriedade. Para o número 2, especialmente, uma amostra aleatória provavelmente não exibirá essa característica. Talvez você possa fazer um tamanho de amostra maior? Gostaria de sugerir algo entre 100 e 300.
Joel Cornett
Um método de medição melhor seria uma média móvel, como a média sobre uma grande janela para o bitstream não vai mudar muito (e deve ser em torno de 0,5)
Joel Cornett
@JoelCornett Obrigado pelo conselho. Eu não sei muito sobre testes de aleatoriedade. Vou mudar o número 2 para outra coisa e estou lendo sobre médias móveis.
PhiNotPi 02/08/2012
1
Sem problemas. Seqüências aleatórias tendem a se aglomerar e não são distribuídas uniformemente, esse é um fato que às vezes é usado na contabilidade para detectar fraudes. (Números fraudulentos, muitas vezes, ser muito bem distribuída, porque as pessoas inventando-lhes uniformidade erro de aleatoriedade)
Joel Cornett
Posso usar funções criptográficas integradas (como AES ou SHA-2)?
CodesInChaos

Respostas:

8

C, 61

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

Sim, eu sei que não é código de golfe. Obviamente, isso é uma anti-solução ... mas com certeza atende aos seus critérios.

fora | cabeça -c840
$ ./a.out | cabeça -c840 | perl tester.pl
Teste 2: 1 (1) 2.93333333333333 (3) 3.1 (3) 0.966666666666667 (1)
Teste 3: 214 99 71 24 7 5 1 1 2 2
Teste 4: 0.495238095238095

A duração do período é 2²⁹.

deixou de girar contra-relógio
fonte
6
Isso mostra como é difícil diferenciar aleatoriamente algo que é amplamente conhecido por ser um dos piores geradores de números aleatórios existentes. +1.
PhiNotPi
8

Mathematica 78 53 caracteres

Os dígitos da representação binária de Pi parecem se comportar como se fossem produzidos caoticamente, embora isso não seja comprovado.

A rotina simples a seguir retorna deterministicamente como uma sequência os dígitos binários de pi, correspondentes aos ddígitos decimais:

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

Uso

Se solicitarmos a contrapartida de 301 dígitos decimais de Pi, receberemos 1000 dígitos binários.

f[301]
StringLength[%]

(* out *)
1100100100001111110110101010001000100001011010001100001000110100110001001100011001100010100010111000000011011100000111001101000100101001000000100100111000001000100010100110011111001100011101000000001000001011101111101010011000111011000100111001101100100010010100010100101000001000011110011000111000110100000001001101110111101111100101010001100110110011110011010011101001000011000110110011000000101011000010100110110111110010010111110001010000110111010011111110000100110101011011010110110101010001110000100100010111100100100001011011010101110110011000100101111001111110110001101111010001001100010000101110100110100110001101111110110101101011000010111111111101011100101101101111010000000110101101111110110111101110001110000110101111111011010110101000100110011111101001011010111010011111001001000001000101111100010010110001111111100110010010010010100001100110010100011110110011100100010110110011110111000010000000000111110010111000101000010110001110111111000001011001100011011010010010000011011000011100011

1000 (* characters *)

Como Pi é um número irracional, não há período. No entanto, haverá restrições práticas devido ao hardware que está sendo executado.

Teste 1 Parece bom para mim.

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

Verificação mais completa:

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

Teste 3: Executa

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

Eu executei um grande número de casos para verificar sistematicamente a distribuição de execuções. Em aproximadamente 3 milhões de dígitos binários, havia 830k execuções de 1, 416k execuções de 2, 208k execuções de 3, 104k execuções de 4, etc.

corre 2 Teste 4: correspondência da primeira e da segunda metade dos dados

As correspondências são os 212 casos de 0 e 2; as incompatibilidades são os 208 casos em que a soma dos respectivos dígitos é 1.

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

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

Cronometragem

Demora menos de dois segundos para calcular 3321928 dígitos binários (correspondentes a 10 ^ 6 dígitos decimais).

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

(*out*)
{1.785928,Null}    
3321928
DavidC
fonte
1
Eu sabia que alguém faria isso ...
deixou de girar counterclockwis
1
Frutas baixas, certo?
9139
Você não poderia usar em evez de pisalvar um byte?
pppery
É edistribuído 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é o valor inicial. A amostragem aleatória exibe uma distribuição notavelmente normal. A amostragem aleatória repetida das médias da amostra produziu uma média 0.506e um desvio padrão de .0473(tamanho da amostra de 1000). Infelizmente, a aleatoriedade é altamente sensível à semente inicial. A semente no código acima me deu a melhor aleatoriedade: p

ATUALIZAR

Vamos ver como esse código aguenta os testes do OP:

Teste # 1

Este é um pouco subjetivo ... mas me parece bastante irregular.

Teste # 2

Três 1's: 0,141
Dois 1's: 0,371
Um 1: 0,335
Zero 1's: 0,135

Teste # 3

Executa por tamanho:

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

Teste # 4

Proporção de igualdade: 0,94 Este é um erro de digitação. Será atualizado com o número correto em breve.

Joel Cornett
fonte
1
Você pode remover o espaço em branco antes de 'for'.
Daniero 5/08
2

Haskell 74 58

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

Obrigado a shiona pela simplificação. Resultados:

/ pseudo-aleatório | cabeça -c 1000

./pseudorandom | cabeça -c 1000 | perl test.pl

Teste 2: 0,966666666666667 (1) 2,4 (3) 3,3 (3) 1,33333333333333 (1)

Teste 3: 260 108 66 33 15 11 5 2

Teste 4: 0,495238095238095

Este também é um terrível gerador pseudo-aleatório (semelhante ao usado por von-Neuman). Para aqueles que não estavam cientes concatMap == (=<<) == flip . (>>=)(para listas)

Walpen
fonte
Você pode substituir \x->if odd x then"1"else"0"por show.(`mod`2).
Shion
1

A questão é essencialmente equivalente a "implementar uma cifra de fluxo". Por isso, implementei o RC4, pois é relativamente simples.

Não uso nenhuma chave e largo os primeiros 100000 bits, porque o início do RC4 é um pouco tendencioso, principalmente porque ignorei o cronograma das chaves. Mas eu esperaria que ele passasse no teste mesmo sem isso (economizando 20 caracteres de código).

Normalmente, seria gerado um byte completo por ciclo, mas a conversão para binário é bastante feia em C #, então simplesmente descarto tudo, exceto o bit menos 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);
}

Ou sem espaços:

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 caracteres, funciona no modo de instrução do LinqPad. Para um programa C # completo, adicione o padrão comum.


Também poderíamos usar primitivas criptográficas incorporadas (solução Cheater):

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

(C #, 99 caracteres, funciona no modo de instrução do LinqPad. Para o compilador C # normal, você precisará adicionar um pouco de clichê)

A saída das funções de hash criptográfico é projetada para ser indistinguível de dados aleatórios, então eu espero que ela passe em todos os testes de aleatoriedade (morra mais forte, ...) que você joga nela, mas estou com preguiça de testar.

CodesInChaos
fonte
1

C, 52 caracteres

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

Este é um LFSR de 10 bits, resultados do teste:

$ ./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
Hasturkun
fonte
adeve começar como 1, (supondo que seja chamado sem argumentos). Além disso, você pode furar o a=no meio, algo como a=a/2^-!putchar(49-a%2)%576(tendo algumas liberdades com o algoritmo)
walpen
@walpen: Minha implementação inicial não foi definida a, eu a mudei por causa de " The program must not take any input from any external sources"
Hasturkun
1

Sage / Python

Este programa imprime os dígitos binários mais à direita, comuns a todas as torres de exponenciação suficientemente altas, no formato 3 3 3 3 . . . Por tudo o que poderia ser gerado de maneira viável, esses são os dígitos binários mais à direita do número de Graham . A sequência de dígitos é infinita e não é periódica.

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

Para 1000 dígitos, isso levou menos de 2 segundos; no entanto, o tempo aumentará muito mais rápido do que linearmente no número de dígitos.

Os resultados do teste usando o programa do OP são

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

(Consulte Os dígitos mais à direita de G são aleatórios? Para mais de 32000 dígitos e testes estatísticos adicionais.)

res
fonte
1

Java, 371 317

Com base em um LFSR de 128 bits (os toques de bits são do xilinx app note 52 )

Edição: Eu não estava satisfeito com o uso do BigInteger, então esta versão não. Salvou alguns caracteres. A saída pode ser um pouco menos aleatória, pois eu não conseguia pensar em um bom método de 'propagação'.

Novo código: Argumentos: 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);}}}

Versão antiga: Argumentos: 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);}}}

Nova versão: Exemplo de saída, bits = 100:

011001100111000110010100100111011100100111000111001111110110001001100000100111111010111001100100011
Noé
fonte
1
BTW, suponho que as duas contas Noah deste post sejam a mesma pessoa. Se é assim, você pode pedir um moderador para fundi-los em meta.codegolf.stackexchange.com
Peter Taylor
0

JavaScript - 1ms a 2ms para 1000 bits pseudo-aleatórios (139ms a 153ms para 100000 bits)

Essa solução usa o fato de que as raízes quadradas são irracionais e, portanto, praticamente aleatórias. Basicamente, é necessária a raiz quadrada de 2 para iniciar, a converte em binária, joga fora a parte principal que corresponde à raiz anterior, anexa à seqüência aleatória, repete com o próximo número mais alto (ou volta a 2 se o número repetido e tinha pelo menos 30 bits) e retorna a sequência aleatória assim que for longa o suficiente.

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

Ainda não testei os testes, mas imagino que funcione bem neles. Aqui está um violino para que você possa vê-lo em ação. Nos meus tempos, apenas executei o programa várias vezes e assumi os valores mais rápidos e lentos como os intervalos.

Briguy37
fonte
0

Pitão

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

Deve ter um período de cerca de 2 ^ 512.

Keith Randall
fonte
0

perl, 44 bytes

Eu sei que isso não é código de golfe, mas sempre fui fã de pegar os bits de baixa ordem de uma função quadrática simples, por exemplo:

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

O período é superior a 3 bilhões, mas fiquei sem espaço em disco para calcular mais.

skibrianski
fonte
1
você pode salvar 3 caracteres justapondo constantes numéricas e palavras-chave e também distribuindo esses 4:$x=1/7;print substr($x*=4-4*$x,9,1)%2while 1
ardnew