Que problema melhor para o PCG.SE do que implementar o PCG, um melhor gerador de números aleatórios ? Este novo artigo pretende apresentar um gerador de números aleatórios rápido, difícil de prever, pequeno e estatisticamente ideal.
Sua implementação C mínima é de apenas nove linhas:
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t;
uint32_t pcg32_random_r(pcg32_random_t* rng)
{
uint64_t oldstate = rng->state;
// Advance internal state
rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1);
// Calculate output function (XSH RR), uses old state for max ILP
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
uint32_t rot = oldstate >> 59u;
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}
(de: http://www.pcg-random.org/download.html )
A questão é: você pode fazer melhor?
Regras
Escreva um programa ou defina uma função que implemente o PCG em números inteiros não assinados de 32 bits. Isso é bastante amplo: você pode imprimir uma sequência infinita, definir uma pcg32_random_r
função e estrutura correspondente, etc.
Você deve poder propagar seu gerador de números aleatórios equivalentemente à seguinte função C:
// pcg32_srandom(initstate, initseq)
// pcg32_srandom_r(rng, initstate, initseq):
// Seed the rng. Specified in two parts, state initializer and a
// sequence selection constant (a.k.a. stream id)
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
{
rng->state = 0U;
rng->inc = (initseq << 1u) | 1u;
pcg32_random_r(rng);
rng->state += initstate;
pcg32_random_r(rng);
}
(de pcg_basic.c:37
:)
Chamar o gerador de números aleatórios sem propagá-lo primeiro é um comportamento indefinido.
Para verificar facilmente seu envio, verifique se, quando semeado com initstate = 42
e initseq = 52
, o resultado é 2380307335
:
$ tail -n 8 pcg.c
int main()
{
pcg32_random_t pcg;
pcg32_srandom_r(&pcg, 42u, 52u);
printf("%u\n", pcg32_random_r(&pcg));
return 0;
}
$ gcc pcg.c
$ ./a.out
2380307335
Pontuação
Pontuação padrão. Medido em bytes. O mais baixo é o melhor. Em caso de empate, o envio antecipado vence. Aplicam-se brechas padrão .
Solução de amostra
Compila sob gcc -W -Wall
limpeza (versão 4.8.2).
Compare seu envio com este para garantir que você obtenha a mesma sequência.
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t;
uint32_t pcg32_random_r(pcg32_random_t* rng)
{
uint64_t oldstate = rng->state;
// Advance internal state
rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1);
// Calculate output function (XSH RR), uses old state for max ILP
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
uint32_t rot = oldstate >> 59u;
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
{
rng->state = 0U;
rng->inc = (initseq << 1u) | 1u;
pcg32_random_r(rng);
rng->state += initstate;
pcg32_random_r(rng);
}
int main()
{
size_t i;
pcg32_random_t pcg;
pcg32_srandom_r(&pcg, 42u, 52u);
for (i = 0; i < 16; i++)
{
printf("%u\n", pcg32_random_r(&pcg));
}
return 0;
}
Seqüência:
2380307335
948027835
187788573
3952545547
2315139320
3279422313
2401519167
2248674523
3148099331
3383824018
2720691756
2668542805
2457340157
3945009091
1614694131
4292140870
Respostas:
CJam,
1091071049891 bytesIsso usa alguns caracteres fora do intervalo ASCII, mas todos estão dentro do ASCII estendido, então estou contando cada caractere como um byte (em vez de contar como UTF-8).
Esta é basicamente uma porta exata do código C.
A função de semente é um bloco armazenado em
S
, e a função aleatória é um bloco armazenado emR
.S
esperainitstate
einitseq
na pilha e semeia o PRNG.R
não espera nada na pilha e deixa o próximo número aleatório nela.Como chamar
R
antes de chamarS
é um comportamento indefinido, na verdade eu estou definindoR
dentroS
, então até você usar o bloco de propagação,R
é apenas uma string vazia e inútil.O
state
é armazenado na variávelA
e oinc
é armazenado emB
.Explicação:
E aqui está o equivalente ao equipamento de teste no OP:
que imprime exatamente os mesmos números.
Teste aqui. O Stack Exchange retira os dois caracteres não imprimíveis, para que não funcione se você copiar o snippet acima. Copie o código do contador de caracteres .
fonte
C, 195
Imaginei que alguém deveria postar uma implementação C mais compacta, mesmo que ela não tenha chance de ganhar. A terceira linha contém duas funções:
r()
(equivalente apcg32_random_r()
) es()
(equivalente apcg32_srandom_r()
). A última linha é amain()
função, que é excluída da contagem de caracteres.Embora o compilador se queixe, isso deve funcionar corretamente em uma máquina de 64 bits. Em uma máquina de 32 bits você terá que adicionar mais 5 bytes à mudança
#define L long
para#define L long long
( como neste ideone demonstração ).fonte
srandom
função faz parte do seu envio e deve ser incluída na contagem de caracteres. (Afinal, talvez você possa pensar em uma maneira inteligente de otimizar isso.) Isso aumentaria sua pontuação atual para 197, pela minha conta.Julia,
218199191 bytesRenomear tipos de dados, além de alguns outros truques, me ajudou a reduzir o comprimento em 19 bytes adicionais. Principalmente omitindo duas atribuições do tipo :: Int64 .
Explicação dos nomes (com os nomes correspondentes na versão não destruída abaixo):
Inicialize a semente com o estado 42 e a sequência 52. Devido ao programa menor, é necessário declarar explicitamente o tipo de dados durante a inicialização agora (salvo 14 bytes de código). Você pode omitir a atribuição de tipo explícita em sistemas de 64 bits:
Produza o primeiro conjunto de números aleatórios:
Resultado:
Fiquei realmente surpreso que mesmo minha versão não-Julia de Julia abaixo seja muito menor (543 bytes) do que a solução de amostra em C (958 bytes).
Versão não destruída, 543 bytes
Você inicializa a semente (estado inicial = 42, sequência inicial = 52) com:
Então você pode criar números aleatórios com:
Aqui está o resultado de um script de teste:
Como sou um programador abismal, levei quase um dia para fazê-lo funcionar. A última vez que tentei codificar algo em C (na verdade, C ++) foi há quase 18 anos, mas muito do google-fu finalmente me ajudou a criar uma versão Julia funcional. Devo dizer que aprendi muito durante apenas alguns dias no codegolf. Agora posso começar a trabalhar em uma versão do Piet. Isso vai dar muito trabalho, mas eu realmente quero um (bom) gerador de números aleatórios para Piet;)
fonte