¿Por qué fueron 181783497276652981
y 8682522807148012
elegido en Random.java
?
Aquí está el código fuente relevante de 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);
Por lo tanto, la invocación new Random()
sin ningún parámetro semilla toma el "uniquificador semilla" actual y lo XOR conSystem.nanoTime()
. Luego se usa 181783497276652981
para crear otro uniquificador de semillas que se almacenará para la próxima vez que new Random()
se llame.
Los literales 181783497276652981L
y 8682522807148012L
no se colocan en constantes, pero no aparecen en ningún otro lugar.
Al principio, el comentario me da una pista fácil. La búsqueda en línea de ese artículo produce el artículo real . 8682522807148012
no aparece en el documento, pero 181783497276652981
aparece, como una subcadena de otro número,1181783497276652981
, que tiene 181783497276652981
un 1
prefijo.
El periódico afirma que 1181783497276652981
es un número que ofrece un buen "mérito" para un generador congruencial lineal. ¿Este número simplemente se copió incorrectamente en Java? ¿ 181783497276652981
Tiene un mérito aceptable?
Y por que fue 8682522807148012
elegido?
La búsqueda en línea de cualquiera de los números no proporciona ninguna explicación, solo esta página que también nota el que se ha caído 1
en frente de 181783497276652981
.
¿Se podrían haber elegido otros números que hubieran funcionado tan bien como estos dos números? ¿Por qué o por qué no?
8682522807148012
es un legado de la versión anterior de la clase, como se puede ver en las revisiones realizadas en 2010 . De hecho,181783497276652981L
parece ser un error tipográfico y podría presentar un informe de error.seedUniquifier
puede ser extremadamente competitivo en una caja de 64 núcleos. Un subproceso local habría sido más escalable.Respuestas:
Sí, parece ser un error tipográfico.
Esto podría determinarse utilizando el algoritmo de evaluación presentado en el documento. Pero el mérito del número "original" probablemente sea mayor.
Parece ser aleatorio. Podría ser el resultado de System.nanoTime () cuando se escribió el código.
No todos los números serían igualmente "buenos". Entonces, no.
Estrategias de siembra
Existen diferencias en el esquema de inicialización predeterminado entre las diferentes versiones y la implementación del JRE.
El primero no es aceptable si crea varios RNG seguidos. Si sus tiempos de creación caen en el mismo rango de milisegundos, darán secuencias completamente idénticas. (misma semilla => misma secuencia)
El segundo no es seguro para subprocesos. Varios subprocesos pueden obtener RNG idénticos cuando se inicializan al mismo tiempo. Además, las semillas de inicializaciones posteriores tienden a estar correlacionadas. Dependiendo de la resolución real del temporizador del sistema, la secuencia de semillas podría aumentar linealmente (n, n + 1, n + 2, ...). Como se indica en ¿Cuán diferentes deben ser las semillas aleatorias? y el documento de referencia. Defectos comunes en la inicialización de generadores de números pseudoaleatorios , las semillas correlacionadas pueden generar correlación entre las secuencias reales de múltiples RNG.
El tercer enfoque crea semillas distribuidas aleatoriamente y, por lo tanto, no correlacionadas, incluso entre subprocesos e inicializaciones posteriores. Entonces, los documentos java actuales:
podría extenderse "a través de subprocesos" y "sin correlación"
Calidad de la secuencia de semillas
Pero la aleatoriedad de la secuencia de siembra es tan buena como el RNG subyacente. El RNG usado para la secuencia semilla en esta implementación de Java usa un generador congruencial lineal multiplicativo (MLCG) con c = 0 ym = 2 ^ 64. (El módulo 2 ^ 64 está implícitamente dado por el desbordamiento de enteros largos de 64 bits) Debido al cero cy el módulo de potencia de 2, la "calidad" (duración del ciclo, correlación de bits, ...) es limitada . Como dice el documento, además de la duración total del ciclo, cada bit tiene una duración de ciclo propia, que disminuye exponencialmente para los bits menos significativos. Por lo tanto, los bits inferiores tienen un patrón de repetición más pequeño. (El resultado de seedUniquifier () debe invertirse en bits, antes de truncarlo a 48 bits en el RNG real)
¡Pero es rápido! Y para evitar bucles de comparación y configuración innecesarios, el cuerpo del bucle debe ser rápido. Esto probablemente explica el uso de este MLCG específico, sin adición, sin xoreo, solo una multiplicación.
Y el artículo mencionado presenta una lista de buenos "multiplicadores" para c = 0 y m = 2 ^ 64, como 1181783497276652981.
Considerándolo todo: A por esfuerzo @ JRE-developers;) Pero hay un error tipográfico. (Pero quién sabe, a menos que alguien lo evalúe, existe la posibilidad de que el 1 inicial faltante en realidad mejore el RNG inicial).
Pero algunos multiplicadores son definitivamente peores: "1" conduce a una secuencia constante. "2" conduce a una secuencia de movimiento de un solo bit (de alguna manera correlacionada) ...
La correlación entre secuencias para los RNG es realmente relevante para las simulaciones (de Monte Carlo), donde se instancian e incluso se paralelizan múltiples secuencias aleatorias. Por lo tanto, es necesaria una buena estrategia de siembra para obtener ejecuciones de simulación "independientes". Por lo tanto, el estándar C ++ 11 introduce el concepto de Seed Sequence para generar semillas no correlacionadas.
fuente
seedUniquifier
queda atascado en cero.Si considera que la ecuación utilizada para el generador de números aleatorios es:
Donde X (n + 1) es el siguiente número, a es el multiplicador, X (n) es el número actual, c es el incremento ym es el módulo.
Si observa más a fondo
Random
, a, c y m se definen en el encabezado de la clasey mirando el método en
protected int next(int bits)
el que se implementa la ecuaciónEsto implica que el método
seedUniquifier()
está obteniendo X (n) o, en el primer caso, en la inicialización X (0), que en realidad es8682522807148012 * 181783497276652981
, este valor se modifica más por el valor deSystem.nanoTime()
. Este algoritmo es consistente con la ecuación anterior pero con la siguiente X (0) =8682522807148012
, a =181783497276652981
, m = 2 ^ 64 yc = 0. Pero como el mod m de está preformado por el desbordamiento largo, la ecuación anterior simplemente se convierte enMirando el documento , el valor de a =
1181783497276652981
es para m = 2 ^ 64, c = 0. Entonces parece ser solo un error tipográfico y el valor8682522807148012
de X (0) que parece ser un número aparentemente elegido al azar del código heredado paraRandom
. Como se ve aquí. Pero el mérito de estos números elegidos aún podría ser válido, pero como lo menciona Thomas B. probablemente no sea tan "bueno" como el del artículo.EDITAR: los pensamientos originales a continuación se han aclarado, por lo que se pueden ignorar, pero dejándolos como referencia
Esto me lleva a las conclusiones:
La referencia al artículo no es por el valor en sí, sino por los métodos utilizados para obtener los valores debido a los diferentes valores de a, cy m
Es mera coincidencia que el valor sea el mismo que el 1 inicial y el comentario esté fuera de lugar (aunque todavía estoy luchando por creer esto)
O
Ha habido un malentendido grave de las tablas en el documento y los desarrolladores acaban de elegir un valor al azar, ya que en el momento en que se multiplica, ¿cuál era el punto de usar el valor de la tabla en primer lugar, especialmente porque solo puede proporcionar su poseer el valor de semilla de cualquier manera, en cuyo caso estos valores ni siquiera se tienen en cuenta
Entonces para responder a tu pregunta
Sí, se podría haber usado cualquier número, de hecho, si especifica un valor de inicialización cuando crea una instancia aleatoria, está usando cualquier otro valor. Este valor no tiene ningún efecto sobre el rendimiento del generador, esto está determinado por los valores de a, cym que están codificados dentro de la clase.
fuente
Random
y el documento citado que supere por completo la pregunta original, la editaré pronto, gracias.Según el enlace que proporcionó, han elegido ( después de agregar el 1 faltante :) ) el mejor rendimiento de 2 ^ 64 porque no puede tener un número de 2 ^ 128.
fuente