Por que foram 181783497276652981
e 8682522807148012
escolhidos em Random.java
?
Aqui está o código-fonte relevante do Java SE JDK 1.7:
/**
* Creates a new random number generator. This constructor sets
* the seed of the random number generator to a value very likely
* to be distinct from any other invocation of this constructor.
*/
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
private static long seedUniquifier() {
// L'Ecuyer, "Tables of Linear Congruential Generators of
// Different Sizes and Good Lattice Structure", 1999
for (;;) {
long current = seedUniquifier.get();
long next = current * 181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
private static final AtomicLong seedUniquifier
= new AtomicLong(8682522807148012L);
Portanto, invocar new Random()
sem qualquer parâmetro de semente leva o "uniquificador de semente" atual e executa o XOR com ele System.nanoTime()
. Em seguida, ele usa 181783497276652981
para criar outro uniquificador de semente para ser armazenado na próxima vez que new Random()
for chamado.
Os literais 181783497276652981L
e 8682522807148012L
não são colocados em constantes, mas não aparecem em nenhum outro lugar.
A princípio, o comentário me dá uma pista fácil. A pesquisa online por aquele artigo produz o artigo real . 8682522807148012
não aparece no papel, mas 181783497276652981
aparece - como uma substring de outro número ,, 1181783497276652981
que está 181783497276652981
com um 1
prefixado.
O artigo afirma que 1181783497276652981
é um número que produz um bom "mérito" para um gerador de congruência linear. Este número foi simplesmente copiado incorretamente para o Java? Tem 181783497276652981
um mérito aceitável?
E por que foi 8682522807148012
escolhido?
A pesquisa online por qualquer um dos números não produz nenhuma explicação, apenas esta página que também mostra a queda 1
na frente de 181783497276652981
.
Poderiam ter sido escolhidos outros números que funcionassem tão bem quanto esses dois números? Por que ou por que não?
8682522807148012
é um legado da versão anterior da turma, como pode ser visto nas revisões feitas em 2010 . Na181783497276652981L
verdade, parece ser um erro de digitação e você pode enviar um relatório de bug.seedUniquifier
pode ser extremamente disputado em uma caixa de 64 núcleos. Um thread local teria sido mais escalonável.Respostas:
Sim, parece ser um erro de digitação.
Isso pode ser determinado usando o algoritmo de avaliação apresentado no artigo. Mas o mérito do número "original" é provavelmente maior.
Parece ser aleatório. Pode ser o resultado de System.nanoTime () quando o código foi escrito.
Nem todos os números seriam igualmente "bons". Então não.
Estratégias de Semeadura
Existem diferenças no esquema de propagação padrão entre diferentes versões e implementação do JRE.
O primeiro não é aceitável se você criar vários RNGs em uma linha. Se os tempos de criação caírem no mesmo intervalo de milissegundos, eles darão sequências completamente idênticas. (mesma semente => mesma sequência)
O segundo não é thread-safe. Vários threads podem obter RNGs idênticos ao inicializar ao mesmo tempo. Além disso, as sementes de inicializações subsequentes tendem a ser correlacionadas. Dependendo da resolução real do temporizador do sistema, a sequência de sementes pode ser linearmente crescente (n, n + 1, n + 2, ...). Conforme declarado em Quão diferentes as sementes aleatórias precisam ser? e o artigo referenciado Defeitos comuns na inicialização de geradores de número pseudo-aleatório , sementes correlacionadas podem gerar correlação entre as sequências reais de múltiplos RNGs.
A terceira abordagem cria sementes distribuídas aleatoriamente e, portanto, não correlacionadas, mesmo entre threads e inicializações subsequentes. Portanto, a documentação atual do java:
pode ser estendido por "entre threads" e "não correlacionado"
Qualidade da sequência de sementes
Mas a aleatoriedade da sequência de propagação é tão boa quanto o RNG subjacente. O RNG usado para a sequência de semente nesta implementação java usa um gerador congruencial linear multiplicativo (MLCG) com c = 0 e m = 2 ^ 64. (O módulo 2 ^ 64 é implicitamente dado pelo estouro de inteiros longos de 64 bits) Por causa do zero ce do módulo de potência de 2, a "qualidade" (comprimento do ciclo, correlação de bits, ...) é limitada . Como diz o artigo, além do comprimento total do ciclo, cada bit tem um comprimento de ciclo próprio, que diminui exponencialmente para bits menos significativos. Assim, os bits mais baixos têm um padrão de repetição menor. (O resultado de seedUniquifier () deve ser invertido em bits, antes de ser truncado para 48 bits no RNG real)
Mas é rápido! E para evitar loops de comparação e configuração desnecessários, o corpo do loop deve ser rápido. Isso provavelmente explica o uso desse MLCG específico, sem adição, sem xoragem, apenas uma multiplicação.
E o referido trabalho apresenta uma lista de bons "multiplicadores" para c = 0 em = 2 ^ 64, como 1181783497276652981.
Resumindo: A para o esforço @ JRE-developers;) Mas há um erro de digitação. (Mas quem sabe, a menos que alguém avalie, há a possibilidade de que o 1 líder ausente na verdade melhore o RNG de semeadura.)
Mas alguns multiplicadores são definitivamente piores: "1" leva a uma sequência constante. "2" leva a uma sequência de movimento de bit único (de alguma forma correlacionada) ...
A correlação inter-sequência para RNGs é realmente relevante para Simulações (Monte Carlo), onde várias sequências aleatórias são instanciadas e até mesmo paralelizadas. Portanto, uma boa estratégia de semeadura é necessária para obter execuções de simulação "independentes". Portanto, o padrão C ++ 11 introduz o conceito de uma sequência de sementes para gerar sementes não correlacionadas.
fonte
seedUniquifier
pare em zero.Se você considerar que a equação usada para o gerador de números aleatórios é:
Onde X (n + 1) é o próximo número, a é o multiplicador, X (n) é o número atual, c é o incremento e m é o módulo.
Se você olhar mais adiante
Random
, a, c e m são definidos no cabeçalho da classee olhando para o método em que
protected int next(int bits)
a equação é implementadaIsso implica que o método
seedUniquifier()
está realmente obtendo X (n) ou no primeiro caso na inicialização X (0) que é8682522807148012 * 181783497276652981
, na verdade , este valor é então modificado pelo valor deSystem.nanoTime()
. Este algoritmo é consistente com a equação acima, mas com o seguinte X (0) =8682522807148012
, a =181783497276652981
, m = 2 ^ 64 e c = 0. Mas como o mod m de é pré-formado pelo longo estouro, a equação acima se tornaOlhando para o papel , o valor de a =
1181783497276652981
é para m = 2 ^ 64, c = 0. Portanto, parece ser apenas um erro de digitação e o valor8682522807148012
de X (0), que parece ser um número aparentemente escolhido aleatoriamente do código legado paraRandom
. Como visto aqui. Mas o mérito desses números escolhidos ainda pode ser válido, mas como mencionado por Thomas B. provavelmente não tão "bom" quanto o do papel.EDITAR - Os pensamentos abaixo originais foram esclarecidos, então podem ser desconsiderados, mas deixando-os para referência
Isso me leva às conclusões:
A referência ao artigo não é para o valor em si, mas para os métodos usados para obter os valores devido aos diferentes valores de a, c e m
É mera coincidência que o valor seja o mesmo, exceto o 1 inicial e o comentário esteja fora do lugar (embora ainda esteja lutando para acreditar nisso)
OU
Houve um sério mal-entendido sobre as tabelas no papel e os desenvolvedores apenas escolheram um valor aleatoriamente quando ele é multiplicado, qual era o objetivo de usar o valor da tabela em primeiro lugar, especialmente porque você pode apenas fornecer seu próprio valor de semente de qualquer maneira, caso em que esses valores nem são levados em consideração
Então, para responder sua pergunta
Sim, qualquer número poderia ter sido usado, na verdade, se você especificar um valor de semente quando instanciar Random, você está usando qualquer outro valor. Este valor não tem nenhum efeito no desempenho do gerador, ele é determinado pelos valores de a, c e m que são codificados dentro da classe.
fonte
Random
e ao artigo citado que ultrapassou completamente a questão original, irei editar em breve, obrigado.De acordo com o link que você forneceu, eles escolheram ( depois de adicionar o 1 :) que faltava ) o melhor rendimento de 2 ^ 64 porque long não pode ter um número de 2 ^ 128
fonte