repetir positivos numeros numero negativos metodo generar cifras arreglo aleatorios aleatorio java random permutation random-seed java.util.random

metodo - numeros aleatorios negativos y positivos en java



¿Es java.util.Random realmente que aleatorio? ¿Cómo puedo generar 52! ¿Secuencias posibles(factoriales)? (8)

En general, un generador de números pseudoaleatorios (PRNG) no puede elegir entre todas las permutaciones de una lista de 52 elementos si la longitud de su estado es inferior a 226 bits.

java.util.Random implementa un algoritmo con un módulo de 2 48 ; por lo tanto, su estado de longitud es de solo 48 bits, mucho menor que los 226 bits a los que me he referido. Deberá usar otro PRNG con una longitud de estado mayor, específicamente, uno con un período de 52 factorial o mayor.

Véase también "Barajar" en mi artículo sobre generadores de números aleatorios .

Esta consideración es independiente de la naturaleza del PRNG; se aplica igualmente a los PRNG criptográficos y no criptográficos (por supuesto, los PRNG no criptográficos son inapropiados cuando se trata de seguridad de la información).

Aunque java.security.SecureRandom permite la java.security.SecureRandom semillas de longitud ilimitada, la implementación de SecureRandom podría usar un PRNG subyacente (por ejemplo, "SHA1PRNG" o "DRBG"). Y depende del período de ese PRNG (y, en menor medida, de la longitud del estado) si es capaz de elegir entre 52 permutaciones factoriales. (Tenga en cuenta que defino "longitud de estado" como el "tamaño máximo de la semilla que un PRNG puede tomar para inicializar su estado sin acortar ni comprimir esa semilla ").

He estado usando Random (java.util.Random) para barajar un mazo de 52 cartas. Hay 52! (8.0658175e + 67) posibilidades. Sin embargo, he descubierto que la semilla para java.util.Random es long , que es mucho más pequeña en 2 ^ 64 (1.8446744e + 19).

Desde aquí, sospecho si java.util.Random es realmente aleatorio ; ¿Es realmente capaz de generar los 52! posibilidades?

Si no, ¿cómo puedo generar de manera confiable una mejor secuencia aleatoria que pueda producir los 52! posibilidades?


La selección de una permutación aleatoria requiere simultáneamente más y menos aleatoriedad de lo que implica su pregunta. Dejame explicar.

La mala noticia: necesita más aleatoriedad.

La falla fundamental en su enfoque es que está tratando de elegir entre ~ 2 226 posibilidades usando 64 bits de entropía (la semilla aleatoria). Para elegir de manera justa entre ~ 2 226 posibilidades, tendrá que encontrar una manera de generar 226 bits de entropía en lugar de 64.

Hay varias formas de generar bits aleatorios: hardware dedicado , instrucciones de CPU , interfaces de sistema operativo , servicios en línea . Ya existe una suposición implícita en su pregunta de que, de alguna manera, puede generar 64 bits, así que solo haga lo que fuera a hacer, solo cuatro veces, y done los excedentes a la caridad. :)

La buena noticia: necesita menos aleatoriedad.

Una vez que tienes esos 226 bits aleatorios, el resto se puede hacer de manera determinista y por lo tanto las propiedades de java.util.Random pueden hacerse irrelevantes . Aquí es cómo.

Digamos que generamos los 52! Permutaciones (soportar conmigo) y ordenarlas lexicográficamente.

Para elegir una de las permutaciones, todo lo que necesitamos es un solo entero aleatorio entre 0 y 52!-1 . Ese entero es nuestro 226 bits de entropía. Lo usaremos como un índice en nuestra lista ordenada de permutaciones. Si el índice aleatorio se distribuye de manera uniforme, no solo se le garantiza que todas las permutaciones se pueden elegir, sino que se elegirán de manera equipotrabable (lo que es una garantía más fuerte que la pregunta).

Ahora, realmente no necesitas generar todas esas permutaciones. Puede producir uno directamente, dada su posición elegida al azar en nuestra lista ordenada hipotética. Esto se puede hacer en tiempo O (n 2 ) usando el código Lehmer [1] (vea también las permutaciones de numeración y el sistema de números factoriadic ). La n aquí es el tamaño de tu mazo, es decir, 52.

Hay una implementación de C en esta respuesta de . Hay varias variables enteras allí que se desbordarían para n = 52, pero afortunadamente en Java puede usar java.math.BigInteger . El resto de los cálculos se pueden transcribir casi como está:

public static int[] shuffle(int n, BigInteger random_index) { int[] perm = new int[n]; BigInteger[] fact = new BigInteger[n]; fact[0] = BigInteger.ONE; for (int k = 1; k < n; ++k) { fact[k] = fact[k - 1].multiply(BigInteger.valueOf(k)); } // compute factorial code for (int k = 0; k < n; ++k) { BigInteger[] divmod = random_index.divideAndRemainder(fact[n - 1 - k]); perm[k] = divmod[0].intValue(); random_index = divmod[1]; } // readjust values to obtain the permutation // start from the end and check if preceding values are lower for (int k = n - 1; k > 0; --k) { for (int j = k - 1; j >= 0; --j) { if (perm[j] <= perm[k]) { perm[k]++; } } } return perm; } public static void main (String[] args) { System.out.printf("%s/n", Arrays.toString( shuffle(52, new BigInteger( "7890123456789012345678901234567890123456789012345678901234567890")))); }

[1] No debe confundirse con Lehrer . :)


Permítame disculparme por adelantado, porque es un poco difícil de entender ...

En primer lugar, ya sabes que java.util.Random no es completamente aleatorio. Genera secuencias de una manera perfectamente predecible a partir de la semilla. Es completamente correcto que, dado que la semilla tiene solo 64 bits de longitud, solo puede generar 2 ^ 64 secuencias diferentes. Si tuvieras que generar de alguna manera 64 bits aleatorios reales y usarlos para seleccionar una semilla, ¡no podrías usar esa semilla para elegir aleatoriamente entre todos los 52! Posibles secuencias con igual probabilidad.

Sin embargo, este hecho no tiene consecuencias , siempre y cuando no vaya a generar más de 2 ^ 64 secuencias, siempre que no haya nada ''especial'' o ''notablemente especial'' sobre las 2 ^ 64 secuencias que puede generar. .

Digamos que usted tenía un PRNG mucho mejor que usaba semillas de 1000 bits. Imagina que tienes dos formas de inicializarlo: una manera lo inicializarías utilizando la semilla completa y la otra manera reduciría la semilla a 64 bits antes de inicializarla.

Si no supiera qué inicializador fue cuál, ¿podría escribir algún tipo de prueba para distinguirlos? A menos que haya tenido la (mala) suerte de terminar inicializando el malo con los mismos 64 bits dos veces, entonces la respuesta es no. No se podría distinguir entre los dos inicializadores sin un conocimiento detallado de alguna debilidad en la implementación específica de PRNG.

Alternativamente, imagine que la clase Random tenía una matriz de 2 ^ 64 secuencias que se seleccionaron completamente y al azar en algún momento en el pasado distante, y que la semilla era solo un índice en esta matriz.

Por lo tanto, el hecho de que Random use solo 64 bits para su semilla no es necesariamente un problema estadístico, siempre que no haya una posibilidad significativa de que uses la misma semilla dos veces.

Por supuesto, para fines criptográficos , una semilla de 64 bits no es suficiente, porque lograr que un sistema use la misma semilla dos veces es computacionalmente factible.

EDITAR:

Debo añadir que, aunque todo lo anterior es correcto, la implementación real de java.util.Random no es impresionante. Si está escribiendo un juego de cartas, quizás use la API MessageDigest para generar el hash SHA-256 de "MyGameName"+System.currentTimeMillis() , y use esos bits para barajar el mazo. Según el argumento anterior, siempre que sus usuarios no estén realmente jugando, no tiene que preocuparse de que currentTimeMillis devuelva mucho. Si sus usuarios realmente están apostando, entonces use SecureRandom sin semilla.


Si considera que el número es solo una matriz de bits (o bytes), tal vez podría usar las soluciones (seguras) Random.nextBytes sugeridas en esta pregunta de desbordamiento de pila y luego asignar la matriz a un new BigInteger(byte[]) .


Solución corta que es esencialmente la misma de dasblinkenlight:

// Java 7 SecureRandom random = new SecureRandom(); // Java 8 SecureRandom random = SecureRandom.getInstanceStrong(); Collections.shuffle(deck, random);

No tienes que preocuparte por el estado interno. Explicación larga por qué:

Cuando crea una instancia de SecureRandom esta manera, accede a un generador de números aleatorios verdaderos específico del sistema operativo. Esto es un conjunto de entropía donde se accede a valores que contienen bits aleatorios (por ejemplo, para un temporizador de nanosegundos, la precisión de nanosegundos es esencialmente aleatoria) o un generador de números de hardware interno.

Esta entrada (!) Que aún puede contener trazas espurias se alimenta a un hash criptográficamente fuerte que elimina esas trazas. ¡Esa es la razón por la que se usan esos CSPRNG, no para crear esos números ellos mismos! SecureRandom tiene un contador que rastrea la cantidad de bits que se usaron ( getBytes() , getLong() etc.) y rellena SecureRandom con bits de entropía cuando es necesario .

En resumen: simplemente olvide las objeciones y use SecureRandom como verdadero generador de números aleatorios.


Su análisis es correcto: sembrar un generador de números pseudoaleatorios con cualquier semilla específica debe producir la misma secuencia después de un orden aleatorio, limitando el número de permutaciones que podría obtener a 2 64 . Esta afirmación es fácil de verificar experimentalmente llamando a Collection.shuffle dos veces, pasando un objeto Random inicializado con la misma semilla y observando que las dos aleatorias aleatorias son idénticas.

Una solución a esto, entonces, es usar un generador de números aleatorios que permita una semilla más grande. Java proporciona SecureRandom clase SecureRandom que podría inicializarse con una matriz de byte[] de tamaño virtualmente ilimitado. Luego puede pasar una instancia de SecureRandom a Collections.shuffle para completar la tarea:

byte seed[] = new byte[...]; Random rnd = new SecureRandom(seed); Collections.shuffle(deck, rnd);


Un algoritmo muy simple es aplicar SHA-256 a una secuencia de enteros que se incrementan de 0 en adelante. (Si se desea, se puede agregar una sal para "obtener una secuencia diferente".) Si suponemos que la salida de SHA-256 es "tan buena como" enteros distribuidos uniformemente entre 0 y 2 256 - 1, entonces tenemos suficiente entropía para el tarea.

Para obtener una permutación de la salida de SHA256 (cuando se expresa como un entero), simplemente se necesita reducir el módulo 52, 51, 50 ... como en este pseudocódigo:

deck = [0..52] shuffled = [] r = SHA256(i) while deck.size > 0: pick = r % deck.size r = floor(r / deck.size) shuffled.append(deck[pick]) delete deck[pick]


Voy a tomar un poco de una manera diferente en esto. Tienes razón en tus suposiciones: ¡tu PRNG no podrá alcanzar los 52! posibilidades

La pregunta es: ¿cuál es la escala de su juego de cartas?

¿Si estás haciendo un juego simple al estilo klondike? Entonces definitivamente no necesitas los 52! posibilidades En vez de eso, míralo así: un jugador tendrá 18 quintillones de juegos distintos. Incluso teniendo en cuenta el "Problema de cumpleaños", tendrían que jugar miles de millones de manos antes de encontrarse con el primer juego duplicado.

¿Si estás haciendo una simulación de monte-carlo? Entonces probablemente estés bien. Es posible que tenga que lidiar con artefactos debido a la ''P'' en PRNG, pero probablemente no tendrá problemas simplemente debido a un bajo espacio inicial (nuevamente, está viendo quintillones de posibilidades únicas). Por otro lado, si está trabajando con una gran cantidad de iteraciones, entonces, sí, su bajo espacio inicial podría ser un factor decisivo.

¿Si estás haciendo un juego de cartas multijugador, particularmente si hay dinero en la línea? Luego, tendrá que hacer un poco de googlear sobre cómo los sitios de póquer en línea manejan el mismo problema que usted pregunta. Porque si bien el problema del espacio inicial reducido no es perceptible para el jugador promedio, es explotable si vale la pena el tiempo invertido. (Todos los sitios de póquer pasaron por una fase en la que sus PRNG fueron "pirateados", permitiendo que alguien vea las cartas ocultas de todos los demás jugadores, simplemente deduciendo la semilla de las cartas expuestas). No busque simplemente un PRNG mejor, tendrá que tratarlo tan seriamente como un problema de Crypto.