Crie um gerador de números aleatórios que passe nos testes de Diehard

50

Embora existam muitas perguntas sobre código de golfe envolvendo aleatoriedade, ainda não vi uma que realmente solicite a construção de um gerador de números pseudoaleatórios algorítmicos. Existe um que pede para você gerar um fluxo de bits, mas os testes de aleatoriedade fornecidos nesse não foram muito rigorosos e não são códigos de golfe.

O programa que você escreve terá uma única função que pode ser chamada que retornará um número inteiro aleatório de 0 a 4294967295. Essa função não deve chamar bibliotecas ou outras funções que também não foram gravadas como parte do programa, especialmente chamadas para / dev / random ou a biblioteca rand () interna de um idioma. Mais especificamente, você está limitado aos operadores básicos do idioma em que está trabalhando, como aritmética, acesso à matriz e instruções de controle de fluxo condicional.

A pontuação do seu programa é calculada da seguinte forma:

Score = C / R

Onde C é o comprimento do código em caracteres e R é o número de testes de Diehard que o seu gerador passa (se o gerador de números aleatórios não passar em pelo menos um teste de Diehard, sua pontuação é infinita e é desqualificada). Seu gerador passa no teste Diehard se o arquivo gerado fornecer uma faixa de valores P que parecem distribuídos uniformemente ao longo do intervalo [0, 1).

Para calcular R, use seu gerador de números aleatórios com sua semente padrão para gerar um arquivo de dados binários de 16 MB. Cada chamada da função retorna quatro bytes; se sua função for muito lenta para retornar bytes, isso fará com que uma troca atinja uma pontuação baixa pela dificuldade em testar. Em seguida, execute-o através dos testes Diehard e verifique os valores-P fornecidos. (Não tente implementá-las você mesmo; use as fornecidas aqui )

Menor pontuação ganha, é claro.

Joe Z.
fonte
É permitido código que requer conectividade com a Internet? (não vou acesso de qualquer função aleatória on-line, mas talvez de ping ou os valores de uma chamada API)
elssar
"Esta função não deve chamar bibliotecas ou outras funções que também não foram escritas como parte do programa." Isso inclui funções de conectividade com a Internet. Sua geração deve ser puramente algorítmica.
Joe Z.
O pacote obstinado espera arquivos de entrada de 10 a 11 MB.
Primo
O link para os testes parece estar quebrado, eis uma alternativa possível.
usar o seguinte código
Como devo fazer isso para a minha resposta à prova de falhas cerebrais (removida abaixo)? Eu acho que o código é muito lento para ser prático
Christopher

Respostas:

6

Mathematica, 32/15 = 2,133

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

Uma implementação direta do BBS .

Arquivo binário gerado com:

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

Resumo dos Resultados:

 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

Cheio random.binaqui.

Arquivo de log completo aqui.

2012rcampion
fonte
28!-67é um tanto proibitivo. Existe um valor menor que caberia em um número inteiro de 64 bits?
Primo
@primo Como Python, os números inteiros do Mathematica são de precisão arbitrária por padrão, portanto, não causam problemas.
usar o seguinte código
Eu estava pensando especificamente para a portabilidade em C.
primo
@primo gmplib.org
2012rcampion
21

Perl 28/13 ~ 2,15

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

arquivo de log aqui

Perl 29/13 ~ 2.23

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

arquivo de log aqui

Isso é uma variação de um Xorshift , usando a divisão de ponto flutuante em vez de um deslocamento à direita. Ambos foram aprovados em 13 de 15 testes, sendo reprovados apenas nos testes 6 e 7.

Não sei exatamente quanto tempo dura o ciclo, mas como o código a seguir não termina em um curto período de tempo, é provável que o 2 32 esteja completo :

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

Perl 39/10 = 3,9

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

Nota: se você estiver procurando por um PRNG de estilo Blum-Blum-Shub, a solução de Keith Randall é muito melhor do que qualquer um deles.

Como na minha solução original abaixo, esta também é uma implementação do Blum Blum Shub, com uma grande diferença. Eu uso um módulo um pouco maior que 2 32 ( M = 50971 • 84263 ) e, sempre que um valor é encontrado, ele não é um número inteiro válido de 32 bits (ou seja, maior que 2 32 ), ele retorna o próximo valor no rotação em vez disso. Em essência, esses valores são eliminados, deixando o restante da rotação imperturbável, resultando em uma distribuição quase uniforme.

Parece ter ajudado. Além de passar nos mesmos 9 testes de antes, agora também passa no teste de Distância Mínima de forma convincente. Um arquivo de log de amostra pode ser encontrado aqui .


Perl 33/9 ~ 3,67 (Inválido?)

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

Nota: esta solução pode ser considerada inválida, pois os 0,00037% mais altos do intervalo nunca serão observados.

Uma implementação rápida e suja do Blum Blum Shub . Eu reivindico os seguintes resultados:

 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

Um arquivo de log de amostra pode ser encontrado aqui , fique à vontade para contestar qualquer um dos resultados. O arquivo para diehard pode ser gerado da seguinte maneira:

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

e, em seguida, canalize a saída em um arquivo. Parece que a Distância mínima pode ter passado, mas se você executá-la várias vezes, é sempre muito próxima de 1,0 , o que indica falha.


Detalhes

Em geral, o Blum Blum Shub é um péssimo PRNG, mas seu desempenho pode ser melhorado com a escolha de um bom módulo. O M que escolhi é 7027 • 611207 . Ambos os fatores primos, p e q , têm resíduo modular 3 (mod 4) e gcd (φ (p-1), φ (q-1)) = 2 , que é o mais baixo possível.

Embora esses sejam os únicos critérios listados na página da wiki, isso não parece suficiente. Quase todo o módulo que tentei falhou em todos os testes. Mas há alguns que passarão em alguns dos testes, e o que eu escolhi parece ser excepcionalmente bom, por qualquer motivo.

Como observação final, o Teste 5, por si só, parece ser um bom indicador de quão bom é o PRNG. Se ele quase não passar no Teste 5, falhará espetacularmente com o restante.


BÔNUS: Perl 62/14 ≈ 4.43

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

Apenas para geeks, esta é uma versão de 32 bits do PRNG usada no Tetris for NES original. Surpreendentemente, ele passa em 14 dos 15 testes!

 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

Arquivo de log de amostra pode antes aqui .

É certo que o 1..37bit não é uma transcrição exata. Na versão original, a rotina de entropia é atualizada 60 vezes por segundo e depois consultada em intervalos aleatórios, em grande parte dependente da entrada do usuário. Para quem quer desmontar a ROM, a rotina de entropia começa em 0xAB47.

Pseudo-código no estilo 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
primo
fonte
Sim, notei que seu algoritmo "falhou" no teste de fluxo de bits, mas na verdade tinha alguns valores abaixo de 0,999999. Ainda assim, seus testes parecem precisos.
Joe Z.
Porém, há um problema: números de 4294951589 a 4294967295 não têm chance de ocorrer (embora suponha que seja parte do motivo pelo qual ele falhou em alguns dos testes de Diehard).
Joe Z.
1
@ JoeZeng sim, isso é um problema. É mais evidente no Teste 5: a primeira execução tem 151k de palavras ausentes e o restante delas apenas 143k. Uma solução seria escolher um módulo ligeiramente maior que 2 ^ 32 e permitir que os valores grandes demais fossem reduzidos a zero, mas não consegui encontrar um que funcionasse bem. Se eu fizer, atualizarei a postagem.
Primo
7

Python, 46/15 = 3.0666

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

Usa exponenciação modular para gerar aleatoriedade. 2 ** 32-5 é o maior primo menor que 2 ^ 32. (O mesmo acordo com a impossibilidade de executar o teste nº 2).

Keith Randall
fonte
Você pode colar um arquivo de log?
Primo
Faça o login aqui: codepad.org/ZWhoGe0t
Keith Randall
1
Janelas estúpidas. Ele estava convertendo todas as ocorrências de \re \npara \r\n, o que obviamente distorce os resultados. Uma correção é escrever o arquivo diretamente usando f = open('file.bin', 'wb')e f.write.
Primo
Essa nova pontuação diminui a pontuação anterior, agora é a resposta aceita.
Joe Z.
Essa nova pontuação foi rebaixada mais uma vez, então mudei a resposta aceita.
Joe Z.
4

Ruby, 32/15 = 2,1333

Esta é a solução de Keith Randall, implementada em Ruby.

$v=3;def R;$v=$v**3%(2**32-5)end
YenTheFirst
fonte
@JoeZ Esta parece ser a nova resposta mais baixa, associada à nova resposta do 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);}

Isso passou em todos os testes.

Com menos caracteres, ele passa no TestU01.

Resultado: 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();
    }
Andrew P.
fonte
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;}

Resultados

Passa em todos, exceto no teste nº 6
Veja os resultados em http://codepad.org/k1NSoyQW

Explicação

O C # simplesmente não pode competir com Ruby e Python pela concisão, como sempre, mas eu gostei de tentar. Certamente existem outros valores que também funcionarão (ou seja, o valor inicial para j = 999 e o divisor = 277). Eu os escolhi após uma breve experimentação.

Com wrapper de criação de arquivo

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

}
Richard II
fonte
1

Python, 41/15 = 2.73333

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

É meio trapacear usando a função de hash embutida, mas é embutida, então não há mais trapaça do que usar outras embutidas, como len. Por outro lado, me dói ter que pagar pela global v;declaração ...

Passa em todos os testes da Diehard (tive um problema com o teste nº 2, SEGVs na minha máquina OSX. Para minha pontuação, suponho que ele passará).

Aqui está um driver para gerar o arquivo de 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))
Keith Randall
fonte
"Esta função não deve chamar bibliotecas ou outras funções que também não foram escritas como parte do programa, especialmente chamadas para / dev / random ou a biblioteca rand () interna de um idioma." Sinto muito, mas isso desqualifica sua entrada.
Joe Z.
Para ser claro, "len" também desqualificaria sua entrada.
Joe Z.
Onde você desenha a linha? É +uma função interna e, portanto, desqualificada?
Keith Randall
6
Mas em muitos idiomas, operadores e funções são idênticos. Veja +e __add__em python, ou sobrecarga de operador em c ++. Eu sei que estou meio que quebrando cabelos, então considere este exemplo. Em python posso criar um mapa como este {'a':5}:? Você provavelmente dirá que sim, mas considere que, debaixo das cobertas, hash('a')é chamado quando você faz isso.
Keith Randall
2
Suponho que traçaria a linha quando você precisar se referir sintaticamente à função dessa maneira. Se você puder encontrar um hack no Python que permita acessar diretamente o endereço do mapa sem se referir sintaticamente à função "hash", eu posso aceitá-lo.
Joe Z.
1

C, 38/15 = 2,533

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

Não consegui que os testes Diehard funcionassem na minha máquina, mas ele passou no pacote PractRand com até 8 GB de saída, portanto presumo que passaria em todos eles.

user1502040
fonte
0

Brain-Flak , 344 / (pendente)

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

Experimente online!

Isso funciona bem, mas os links de testes obstinados estão todos quebrados :( então, até obtermos novos, não tenho uma pontuação final

Isso usa o Blum Blum Shub PRNG, por isso deve passar na maioria dos casos. Os números usados ​​são grandes o suficiente, nenhum padrão aparecerá nos 16 MB de casos de teste

Christopher
fonte
se isso é inválido, apenas me diga
Christopher
1
Conto com 344. Teorema: Nenhum programa Brain-flak totalmente golfe possui um número ímpar de bytes.
usar o seguinte comando
0

Objetivo-C, 40/1 = 40

Abordagem bastante inteligente, explorando .hashpara enganar um pouco aqui, mas eu gosto

for(int v=9;v=@(v).hash;printf("%i",v));
Albert Renshaw
fonte