En multitud de programas surge la necesidad de generar un número aleatorio. Aunque quizás parezca algo trivial existen varias malas prácticas que pueden comprometer el funcionamiento de nuestro programa si un usuario mal-intencionado sabe como aprovecharse de ellas.

Por ejemplo, en C/C++ es típico encontrar algo así:

n = 1 + rand() % 10;

para generar un número entre 1 y 10. Esto significa que el valor de n únicamente va a depender del último dígito que devuelva rand(). Lo que no sería problématico si rand() devuelve realmente un número pseudo-aleatorio, pero en implementaciones viejas (y modernas mal hechas) los últimos bits de rand() son no-aleatorios. Por ello es recomendable utilizar bits de mayor orden con algo similar a esto:

n = (int) 1 + 10 * rand() / (RAND_MAX + 1.0);

Si utilizamos una mala impletentación de rand() y un intervalo pequeño, este método es mucho mejor que el anterior ya que utilizamos los bits de mayor orden.

Otro problema importante de la generación de números aleatorios está basado en el Principio_del_palomar. Si tenemos un generador aleatorio para un intervalo de 1 a 10 y suponemos que reparte la probabilidad equitativamente entre los 10 números (mal generador si no) podemos reducir el intervalo entre 1 y 2. Utilizando el típico 1 + rand() % 2 tendríamos algo así:

Valores de rand() para obtener 1:

2,4,6,8,10

Valores de rand() para obtener 2:

1,3,5,7,9

Un total de 5 valores para cada número. Esto está bien pero, ¿qué ocurriría si en vez de 1 y 2 añadimos el número 3? Tendíamos 1 + rand() % 3 y quedaría:

Valores de rand() para obtener 1:

3,6,9

Valores de rand() para obtener  2:

1, 4, 7, 10

Valores de rand() para obtener 3:

2, 5, 8

De esta forma obtenemos una distribución truncada favoreciendo al número 2. Esto ocurre siempre que al hacer la congruencia usemos un número no divisor de RAND_MAX. Con el método anterior evitamos esto ya que al dividir rand() / (RAND_MAX + 1.0) obtenemos un número real en el intervalo [0,1), al multiplicarlo por 3 y convertirlo a entero tendremos 0, 1 o 2 sin haber truncado la probabilidad.

Por último cabe comentar la elección de la semilla para la generación. Los números pseudoaleatorios son totalmente predecibles si conocemos la semilla que origina la secuencia, por ello lo ideal es utilizar algo realmente aleatorio. Lo más común es utilizar la función time(0) aunque hay que tener diversas consideraciones. Lo primero es iniciar la semilla una única vez antes de la primera llamada a rand(). Después, time() tiene una precisión de segundos, es decir, todas las ejecuciones que hagamos en un segundo obtendran la misma secuencia. Esto resulta peligroso en algunas ocasiones, por ejemplo en un juego web que jugaba hace tiempo utilizaban una inicialización de la semilla con este método. Entonces, si por ejemplo al utilizar un item obtenías un beneficio alto, si rapidamente habias actualizado varias ventanas a la vez obtenías en todas el mismo beneficio. Costaba pillarle el truco pero una vez controlado era bastante cantoso el aprovechamiento del fallo  En estos casos se puede utilizar una semilla que dependa dependa de los milisegundos o nanosegundos.

Referencias:

http://www.daniweb.com/forums/thread162830.html#

http://www.eternallyconfuzzled.com/arts/jsw_art_rand.aspx

Algunos dicen (el propio man) que random() es mejor que rand(), para el compilador GCC, pero la página de GNU lo desmiente. Dice que sólo está para portabilidad con BSD y no tiene ninguna ventaja sobre rand().