Perteneciente a rand() % n
ser menos que ideal
Hacer rand() % n
tiene una distribución no uniforme. Obtendrá un número desproporcionado de ciertos valores porque el número de valores no es un múltiplo de 20
A continuación, rand()
suele ser un generador congruencial lineal (hay muchos otros , solo que este es el más implementado, y con parámetros menos que ideales (hay muchas formas de seleccionar los parámetros)). El mayor problema con esto es que a menudo los bits bajos (los que se obtienen con una % 20
expresión de tipo) no son tan aleatorios. Recuerdo uno rand()
de hace años, donde el bit más bajo alternaba de 1
a 0
con cada llamada a rand()
, no era muy aleatorio.
Desde la página de manual de rand (3):
Las versiones de rand () y srand () en la Biblioteca Linux C usan lo mismo
generador de números aleatorios como random () y srandom (), por lo que el orden inferior
los bits deben ser tan aleatorios como los bits de orden superior. Sin embargo, en mayores
implementaciones rand (), y en implementaciones actuales en diferentes
sistemas, los bits de orden inferior son mucho menos aleatorios que los de nivel superior
orden de bits. No utilice esta función en aplicaciones destinadas a ser
portátil cuando se necesita buena aleatoriedad.
Esto puede relegarse ahora a la historia, pero es muy posible que todavía tenga una implementación pobre de rand () escondida en algún lugar de la pila. En cuyo caso, sigue siendo bastante aplicable.
Lo que hay que hacer es usar una buena biblioteca de números aleatorios (que proporcione buenos números aleatorios) y luego pedir números aleatorios dentro del rango que desee.
Un ejemplo de un buen bit de código de número aleatorio (a partir de las 13:00 en el video vinculado)
#include <iostream>
#include <random>
int main() {
std::mt19937 mt(1729); // yes, this is a fixed seed
std::uniform_int_distribution<int> dist(0, 99);
for (int i = 0; i < 10000; i++) {
std::cout << dist(mt) << " ";
}
std::cout << std::endl;
}
Compare esto con:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
srand(time(NULL));
for (int i = 0; i < 10000; i++) {
printf("%d ", rand() % 100);
}
printf("\n");
}
Ejecute ambos programas y compare con qué frecuencia surgen ciertos números (o no aparecen) en esa salida.
Video relacionado: rand () considerado dañino
Algunos aspectos históricos de rand () que causan errores en Nethack que uno debe observar y considerar en sus propias implementaciones:
Problema de Nethack RNG
Rand () es una función muy fundamental para la generación de números aleatorios de Nethack. La forma en que Nethack lo usa es defectuosa o se puede argumentar que lrand48 () produce números pseudoaleatorios mal. (Sin embargo, lrand48 () es una función de biblioteca que usa un método PRNG definido y cualquier programa que lo use debe tener en cuenta las debilidades de ese método).
El error es que Nethack se basa (a veces exclusivamente, como es el caso en rn (2)) en los bits más bajos de los resultados de lrand48 (). Debido a esto, RNG en todo el juego funciona mal. Esto es especialmente notable antes de que las acciones del usuario introduzcan más aleatoriedad, es decir, en la generación de personajes y la creación de primer nivel.
Si bien lo anterior fue de 2003, aún debe tenerse en cuenta, ya que puede no ser el caso de que todos los sistemas que ejecutan el juego previsto sean un sistema Linux actualizado con una buena función rand ().
Si solo está haciendo esto por sí mismo, puede probar qué tan bueno es su generador de números aleatorios escribiendo un código y probando la salida con ent .
Sobre las propiedades de números aleatorios
Hay otras interpretaciones de 'aleatorio' que no son exactamente aleatorias. En una secuencia de datos aleatoria, es bastante posible obtener el mismo número dos veces. Si lanzas una moneda (al azar), es muy posible obtener dos caras seguidas. O tira un dado dos veces y obtén el mismo número dos veces seguidas. O girando una ruleta y obteniendo el mismo número dos veces allí.
La distribución de números.
Al reproducir una lista de canciones, las personas esperan que "aleatorio" signifique que la misma canción o artista no se reproducirá por segunda vez consecutiva. Tener una lista de reproducción en The Beatles dos veces seguidas se considera 'no aleatorio' (aunque es aleatorio). La percepción de que para una lista de reproducción de cuatro canciones tocó un total de ocho veces:
1 3 2 4 1 2 4 3
es más 'aleatorio' que:
1 3 3 2 1 4 4 2
Más sobre esto para la 'mezcla' de canciones: ¿Cómo mezclar canciones?
En valores repetidos
Si no desea repetir valores, hay que considerar un enfoque diferente. Genere todos los valores posibles y barajelos.
Si está llamando rand()
(o cualquier otro generador de números aleatorios), lo está llamando con reemplazo. Siempre puedes obtener el mismo número dos veces. Una opción es desechar los valores una y otra vez hasta que seleccione uno que cumpla con sus requisitos. Señalaré que esto tiene un tiempo de ejecución no determinista y es posible que te encuentres en una situación en la que hay un bucle infinito a menos que comiences a hacer un rastreo más complejo.
Lista y selección
Otra opción es generar una lista de todos los estados válidos posibles y luego seleccionar un elemento aleatorio de esa lista. Encuentra todos los lugares vacíos (que cumplen con algunas reglas) en la habitación y luego elige uno aleatorio de esa lista. Y luego hazlo una y otra vez hasta que hayas terminado.
Barajar
El otro enfoque es barajar como si fuera una baraja de cartas. Comience con todos los espacios vacíos en la sala y luego comience a asignarlos repartiendo los espacios vacíos, uno a la vez, a cada regla / proceso que solicite un espacio vacío. Has terminado cuando te quedas sin cartas o las cosas dejan de pedirlas.