Me gustaría generar números aleatorios únicos entre 0 y 1000 que nunca se repiten (es decir, 6 no aparece dos veces), pero eso no recurre a algo como una búsqueda O (N) de valores anteriores para hacerlo. es posible?
algorithm
math
random
language-agnostic
dicroce
fuente
fuente
O(n)
en el tiempo o la memoria), muchas de las respuestas a continuación son incorrectas, incluida la respuesta aceptada.Respuestas:
Inicialice una matriz de 1001 enteros con los valores 0-1000 y establezca una variable, max, en el índice máximo actual de la matriz (comenzando con 1000). Elija un número aleatorio, r, entre 0 y max, intercambie el número en la posición r con el número en la posición max y devuelva el número ahora en la posición max. Disminuya max en 1 y continúe. Cuando max es 0, vuelva a establecer max al tamaño de la matriz - 1 y comience nuevamente sin la necesidad de reiniciar la matriz.
Actualización: aunque se me ocurrió este método por mi cuenta cuando respondí la pregunta, después de algunas investigaciones me doy cuenta de que esta es una versión modificada de Fisher-Yates conocida como Durstenfeld-Fisher-Yates o Knuth-Fisher-Yates. Dado que la descripción puede ser un poco difícil de seguir, he proporcionado un ejemplo a continuación (usando 11 elementos en lugar de 1001):
La matriz comienza con 11 elementos inicializados en la matriz [n] = n, max comienza en 10:
En cada iteración, se selecciona un número aleatorio r entre 0 y max, se intercambian la matriz [r] y la matriz [max], se devuelve la nueva matriz [max] y se disminuye max:
Después de 11 iteraciones, se han seleccionado todos los números de la matriz, max == 0, y los elementos de la matriz se barajan:
En este punto, max se puede restablecer a 10 y el proceso puede continuar.
fuente
N
iteraciones (11 en este ejemplo) para obtener el resultado deseado cada vez significa que síO(n)
? Como debe hacerN
iteraciones para obtenerN!
combinaciones del mismo estado inicial, de lo contrario, su salida solo será uno de N estados.Puedes hacerlo:
Por lo tanto, esto no requiere una búsqueda de valores antiguos cada vez, pero aún requiere O (N) para la combinación inicial. Pero como Nils señaló en los comentarios, esto se amortiza O (1).
fuente
Utilice un registro de desplazamiento de retroalimentación lineal máxima .
Es implementable en unas pocas líneas de C y en tiempo de ejecución hace poco más que un par de pruebas / ramas, una pequeña adición y un poco de desplazamiento. No es al azar, pero engaña a la mayoría de las personas.
fuente
Podría usar un generador congruencial lineal . Donde
m
(el módulo) sería el primo más cercano mayor que 1000. Cuando obtenga un número fuera del rango, simplemente obtenga el siguiente. La secuencia solo se repetirá una vez que se hayan producido todos los elementos y no tenga que usar una tabla. Sin embargo, tenga en cuenta las desventajas de este generador (incluida la falta de aleatoriedad).fuente
k
en la secuencia nunca pueden ocurrir juntos).Puede usar el cifrado de preservación de formato para cifrar un contador. Su contador solo va de 0 en adelante, y el cifrado utiliza una clave de su elección para convertirlo en un valor aparentemente aleatorio de la raíz y el ancho que desee. Por ejemplo, para el ejemplo en esta pregunta: radix 10, ancho 3.
Los cifrados de bloque normalmente tienen un tamaño de bloque fijo de, por ejemplo, 64 o 128 bits. Pero Format-Preserving Encryption le permite tomar un cifrado estándar como AES y hacer un cifrado de menor ancho, de cualquier radix y ancho que desee, con un algoritmo que todavía es criptográficamente robusto.
Se garantiza que nunca tendrá colisiones (porque los algoritmos criptográficos crean un mapeo 1: 1). También es reversible (un mapeo de 2 vías), por lo que puede tomar el número resultante y volver al valor del contador con el que comenzó.
Esta técnica no necesita memoria para almacenar una matriz aleatoria, etc., lo que puede ser una ventaja en sistemas con memoria limitada.
AES-FFX es un método estándar propuesto para lograr esto. Experimenté con un código básico de Python que se basa en la idea de AES-FFX, aunque no es totalmente compatible; vea el código de Python aquí . Puede, por ejemplo, encriptar un contador a un número decimal de 7 dígitos de aspecto aleatorio, o un número de 16 bits. Aquí hay un ejemplo de radix 10, ancho 3 (para dar un número entre 0 y 999 inclusive) como dice la pregunta:
Para obtener diferentes secuencias pseudoaleatorias no repetitivas, cambie la clave de cifrado. Cada clave de cifrado produce una secuencia pseudoaleatoria no repetitiva diferente.
fuente
k
separados en la secuencia nunca pueden ocurrir juntos).k
?1,2,...,N
con una secuencia de los mismos números en algún otro orden, pero aún constante. Los números se extraen de esta secuencia uno por uno.k
es la cantidad de valores elegidos (el OP no especificó una letra, así que tuve que introducir uno).Para números bajos como 0 ... 1000, crear una lista que contenga todos los números y barajarlo es sencillo. Pero si el conjunto de números para extraer es muy grande, hay otra forma elegante: puede construir una permutación pseudoaleatoria usando una clave y una función hash criptográfica. Consulte el siguiente pseudocódigo de ejemplo C ++ - ish:
Aquí
hash
hay una función pseudoaleatoria arbitraria que asigna una cadena de caracteres a un entero sin signo posiblemente enorme. La funciónrandperm
es una permutación de todos los números dentro de 0 ... pow (2, bits) -1 suponiendo una clave fija. Esto se desprende de la construcción porque cada paso que cambia la variableindex
es reversible. Esto está inspirado en un cifrado Feistel .fuente
hash()
, como se usa en el código anterior, es una función pseudoaleatoria segura, esta construcción demostrará (Luby y Rackoff, 1988) una permutación pseudoaleatoria , que no se puede distinguir de una mezcla aleatoria verdadera con un esfuerzo significativamente menor que un exhaustivo búsqueda de todo el espacio clave, que es exponencial en la longitud de la clave. Incluso para claves de tamaño razonable (digamos, 128 bits), esto está más allá de la potencia informática total disponible en la Tierra.hash( key + "/" + int2str(temp) )
construcción ad hoc anterior con HMAC , cuya seguridad a su vez puede reducirse de manera demostrable a la de la función de compresión hash subyacente. Además, el uso de HMAC podría hacer que es menos probable que alguien intente usar esta construcción por error con una función de hash no criptográfica insegura.)Puede usar mi algoritmo Xincrol descrito aquí:
http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html
Este es un método algorítmico puro para generar números aleatorios pero únicos sin matrices, listas, permutaciones o una gran carga de CPU.
La última versión también permite establecer el rango de números, por ejemplo, si quiero números aleatorios únicos en el rango de 0-1073741821.
Prácticamente lo he usado para
Es abierto, gratis. Darle una oportunidad...
fuente
k
separados en la secuencia nunca pueden ocurrir juntos).Ni siquiera necesita una matriz para resolver este.
Necesitas una máscara de bits y un contador.
Inicialice el contador a cero e increméntelo en llamadas sucesivas. XOR el contador con la máscara de bits (seleccionada aleatoriamente al inicio o fijada) para generar un número psuedorandom. Si no puede tener números que excedan 1000, no use una máscara de bits más ancha que 9 bits. (En otras palabras, la máscara de bits es un número entero no superior a 511).
Asegúrese de que cuando el contador pase 1000, lo restablezca a cero. En este momento, puede seleccionar otra máscara de bits aleatoria, si lo desea, para producir el mismo conjunto de números en un orden diferente.
fuente
Creo que el generador congruencial lineal sería la solución más simple.
y sólo hay 3 restricciones a la una , c y m valores
PD: el método ya se mencionó, pero la publicación tiene supuestos erróneos sobre los valores constantes. Las constantes a continuación deberían funcionar bien para su caso
En su caso, es posible utilizar
a = 1002
,c = 757
,m = 1001
fuente
Aquí hay un código que escribí que usa la lógica de la primera solución. Sé que esto es "independiente del lenguaje", pero solo quería presentar esto como un ejemplo en C # en caso de que alguien esté buscando una solución práctica rápida.
fuente
Este método resulta apropiado cuando el límite es alto y solo desea generar algunos números aleatorios.
Tenga en cuenta que los números se generan en orden ascendente, pero puede barajarlos luego.
fuente
(top,n)=(100,10)
son:(0.01047705, 0.01044825, 0.01041225, ..., 0.0088324, 0.008723, 0.00863635)
. Probé en Python, por lo que pequeñas diferencias en matemáticas podrían desempeñar un papel aquí (me aseguré de que todas las operaciones para calcularr
sean de punto flotante).Podría usar un buen generador de números pseudoaleatorios con 10 bits y tirar 1001 a 1023 dejando 0 a 1000.
Desde aquí obtenemos el diseño para un PRNG de 10 bits.
10 bits, polinomio de retroalimentación x ^ 10 + x ^ 7 + 1 (período 1023)
use un Galois LFSR para obtener un código rápido
fuente
N Los números aleatorios no repetidos serán de complejidad O (n), según se requiera.
Nota: Aleatorio debe ser estático con seguridad de hilo aplicada.
fuente
Digamos que desea repasar las listas barajadas una y otra vez, sin tener el
O(n)
retraso cada vez que comience a barajarlas nuevamente, en ese caso podemos hacer esto:Crear 2 listas A y B, con 0 a 1000, ocupa
2n
espacio.La lista aleatoria A con Fisher-Yates lleva
n
tiempo.Cuando dibuje un número, haga barajar Fisher-Yates de 1 paso en la otra lista.
Cuando el cursor esté al final de la lista, cambie a la otra lista.
Preproceso
Dibujar
fuente
[1,3,4,5,2]
producirá el mismo resultado que barajar[1,2,3,4,5]
.La pregunta ¿Cómo genera eficientemente una lista de K enteros no repetidos entre 0 y un límite superior N está vinculado como un duplicado, y si desea algo que sea O (1) por número aleatorio generado (sin O (n) costo de inicio)) hay un simple ajuste de la respuesta aceptada.
Cree un mapa vacío desordenado (un mapa ordenado vacío tomará O (log k) por elemento) de entero a entero, en lugar de usar una matriz inicializada. Establezca max a 1000 si ese es el máximo,
La única diferencia en comparación con el uso de una matriz inicializada es que la inicialización de elementos se pospone / omite, pero generará exactamente los mismos números del mismo PRNG.
fuente
Otra posibilidad:
Puedes usar una variedad de banderas. Y tome el siguiente cuando ya esté elegido.
Pero, tenga cuidado después de 1000 llamadas, la función nunca terminará, por lo que debe hacer una salvaguarda.
fuente
Aquí hay un código COBOL de muestra con el que puedes jugar.
Puedo enviarle el archivo RANDGEN.exe para que pueda jugar con él y ver si quiere lo que quiere.
fuente
La mayoría de las respuestas aquí no garantizan que no devolverán el mismo número dos veces. Aquí hay una solución correcta:
No estoy seguro de que la restricción esté bien especificada. Se supone que después de otras 1000 salidas se permite repetir un valor, pero ingenuamente permite que 0 siga inmediatamente después de 0, siempre que ambas aparezcan al final y al comienzo de las series de 1000. Por el contrario, es posible mantener una distancia de 1000 otros valores entre repeticiones, al hacerlo, se fuerza una situación en la que la secuencia se repite exactamente de la misma manera cada vez porque no hay otro valor que haya ocurrido fuera de ese límite.
Aquí hay un método que siempre garantiza al menos otros 500 valores antes de que se pueda repetir un valor:
fuente
Cuando N es mayor que 1000 y necesita dibujar K muestras aleatorias, puede usar un conjunto que contenga las muestras hasta ahora. Para cada sorteo, utiliza el muestreo de rechazo , que será una operación "casi" O (1), por lo que el tiempo total de ejecución es casi O (K) con almacenamiento O (N).
Este algoritmo se topa con colisiones cuando K está "cerca" de N. Esto significa que el tiempo de ejecución será mucho peor que O (K). Una solución simple es invertir la lógica para que, para K> N / 2, mantenga un registro de todas las muestras que aún no se han extraído. Cada dibujo elimina una muestra del conjunto de rechazo.
El otro problema obvio con el muestreo de rechazo es que es el almacenamiento de O (N), lo cual es una mala noticia si N está en miles de millones o más. Sin embargo, hay un algoritmo que resuelve ese problema. Este algoritmo se llama algoritmo de Vitter después de su inventor. Se describe el algoritmo. aquí . La esencia del algoritmo de Vitter es que, después de cada sorteo, calcula un salto aleatorio utilizando una cierta distribución que garantiza un muestreo uniforme.
fuente
Fisher Yates
En realidad, es O (n-1) ya que solo necesita un intercambio para los dos últimos.
Esto es C #
fuente
Por favor vea mi respuesta en https://stackoverflow.com/a/46807110/8794687
Es uno de los algoritmos más simples que tienen tiempo medio de complejidad O ( s registro s ), s denota el tamaño de la muestra. También hay algunos enlaces a algoritmos de tablas hash cuya complejidad se afirma que es O ( s ).
fuente
Alguien publicó "creando números aleatorios en Excel". Estoy usando este ideal. Cree una estructura con 2 partes, str.index y str.ran; Para 10 números aleatorios, cree una matriz de 10 estructuras. Establezca str.index de 0 a 9 y str.ran en un número aleatorio diferente.
Ordene la matriz según los valores en arr [i] .ran. El str.index ahora está en un orden aleatorio. A continuación se muestra el código c:
fuente