¿Por qué C ++ rand () parece generar solo números del mismo orden de magnitud?

146

En una pequeña aplicación escrita en C / C ++, estoy enfrentando un problema con la randfunción y tal vez la semilla:

Quiero producir una secuencia de números aleatorios que sean de diferentes órdenes, es decir, con diferentes valores de logaritmo (base 2). Pero parece que todos los números producidos son del mismo orden, fluctuando solo entre 2 ^ 25 y 2 ^ 30.

¿Es porque rand()se siembra con el tiempo Unix que ahora es un número relativamente grande? ¿Qué me estoy olvidando? Estoy sembrando rand()solo una vez al comienzo de la main().

Tallaron Mathias
fuente
77
FWIW entonces, ¿es C o C ++? Si por C / C ++ quiere decir que realmente puede usar C ++, y la mención de C fue aleatoria, tal vez esto en.cppreference.com/w/cpp/numeric/random/binomial_distribution pueda ayudar.
R. Martinho Fernandes
9
Desafortunadamente estabas apostando por el caballo equivocado. La semilla no debería ser tu problema. Su problema fue una distribución esperada incorrecta. Dado que el programador imparcial esperaría rand()devolver números distribuidos uniformemente (la documentación con un alto ranking de Google lo dice explícitamente) no creo que esta pregunta sea útil para futuros lectores. Es por eso que rechazar el voto, pero no dejes que te desanime a usar SO.
Emperador Orionii
12
@ doug65536 "... donde nunca se repite ningún número", ¡eso no es aleatorio! Podría financiar mi retiro en la mesa de dados si mis dados rand () nunca devuelven el mismo número dos veces hasta que se devuelvan todos los números posibles.
Chris Gregg
66
@GalacticCowboy No confunda la periodicidad con una repetición de números individuales. Del artículo de Wikipedia que citó: "un resultado repetido no implica que se haya alcanzado el final del período, ya que su estado interno puede ser mayor que su salida". Sería muy, muy malo si un PRNG produjera un valor y luego se garantizara que no volverá a producir ese valor hasta que se devuelvan todos los valores.
Chris Gregg
12
Doug65536, nadie está peleando. Simplemente están diciendo correctamente que estás equivocado. Un PRNG podría felizmente producir lo siguiente si quisiera un RAND entre 1 y 10: 2 4 7 2 8 1 5 9 7 3 Eso sería completamente válido, a pesar de los múltiples 2s y 7s. Creo que está confundiendo el PRNG con la instalación aleatoria de su iPhone.
Relajarse en Chipre

Respuestas:

479

Solo hay un 3% de números entre 1 y 2 30 que NO están entre 2 25 y 2 30 . Entonces, esto suena bastante normal :)

Debido 2 25 /2 30 = 2 -5 = 1/32 = 0,03125 = 3,125%

C4stor
fuente
36
Sí, buen punto! Hay 31 veces más números entre 2 ^ 25 y 2 ^ 30 que entre 1 y 2 ^ 25 :) gracias por la respuesta rápida. Necesito repensar el programa entonces. Pregunta respondida
Tallaron Mathias
1
@TallaronMathias Considere la posibilidad de truncar el número mediante el >>desplazamiento de bits, esto le dará números más pequeños. (O tomando un módulo con %.)
Sean Allred
13
Esperaría que esto sea obvio para la mayoría de los programadores: cualquier entero sin signo de menos de 2 ^ 25 debe tener sus primeros 7 bits iguales a 0- y si cada bit es aleatorio ...
BlueRaja - Danny Pflughoeft
118
@ BlueRaja-DannyPflughoeft: si las probabilidades fueran obvias, los casinos estarían fuera del negocio.
Brett Hale
26
@BrettHale - Sin embargo, no creo que los programadores sean el objetivo demográfico de un casino.
EkoostikMartin
272

El verde más claro es la región entre 0 y 2 25 ; el verde más oscuro es la región entre 2 25 y 2 30 . Las garrapatas son poderes de 2.

distribución

Casey Chu
fuente
42

Debe ser más preciso: desea valores de logaritmo de base 2 diferentes, pero ¿qué distribución desea para esto? Las funciones estándar rand () generan una distribución uniforme, necesitará transformar esta salida utilizando la función cuantil asociada con la distribución que desee.

Si nos dice la distribución, entonces podemos decirle la quantilefunción que necesita.

Betsabé
fuente
13
+1, distribución es el término crucial. Realmente no tiene sentido hablar de números aleatorios cuando no se sabe nada sobre la distribución. El uniforme es solo un caso especial, aunque importante. Podría ser un buen lugar para señalar varias distribuciones de la biblioteca estándar de C ++ 11.
Leftaroundabout
18

Si desea diferentes órdenes de magnitud, ¿por qué no simplemente intentarlo pow(2, rand())? ¿O tal vez elegir el orden directamente como rand (), como sugirió Harold?

aspiring_sarge
fuente
3
buena idea, pero debe corregir su respuesta usando pow en lugar de ^ (que es el operador lógico xor, no power, en lenguaje C).
kriss
66
Como rand()puede subir RAND_MAX, realmente necesita escalar su número aleatorio para que el resultado no se desborde ...
Floris
@Floris: pero si escala un rango contable pequeño en un rango muy grande, tendrá MUCHOS agujeros, que probablemente no sea lo que OP espera.
André Caron
13

@ C4stor hizo un gran punto. Pero, para un caso más general y más fácil de entender para humanos (base 10): para el rango de 1 a 10 ^ n, ~ 90% de los números son de 10 ^ (n-1) a 10 ^ n, por lo tanto, ~ 99% de los números van de 10 ^ (n-2) a 10 ^ n. Sigue agregando tantos decimales como quieras.

Matemáticas divertidas, si sigues haciendo esto para n, puedes ver que de 1 a 10 ^ n, 99.9999 ...% = 100% de los números son de 10 ^ 0 a 10 ^ n con este método.

Ahora sobre el código, si desea un número aleatorio con órdenes de magnitud aleatorios, de 0 a 10 ^ n, puede hacer:

  1. Genere un pequeño número aleatorio de 0 a n

  2. Si conoce el rango que tiene n, genere un gran número aleatorio de orden 10 ^ k donde k> max {n}.

  3. Corte el número aleatorio más largo para obtener los n dígitos de este gran número aleatorio.

Francisco Presencia
fuente
46
Tiene toda la razón, pero para una respuesta REALMENTE fácil de entender, el OP debe preguntarse por qué el 90% de los números aleatorios entre 1 y 100 son dos dígitos.
Pregunte por Monica
13

La respuesta básica (y correcta) ya fue dada y aceptada arriba: hay 10 números entre 0 y 9, 90 números entre 10 y 99, 900 entre 100 y 999, etc.

Para obtener una manera computacionalmente eficiente de obtener una distribución con una distribución aproximadamente logarítmica, desea desplazar a la derecha su número aleatorio por un número aleatorio:

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

No es perfecto, pero es mucho más rápido que la informática pow(2, rand()*scalefactor). Será "desigual" en el sentido de que la distribución será uniforme para los números dentro de un factor 2 (uniforme para 128 a 255, la mitad de la densidad para 256 a 1023, etc.).

Aquí hay un histograma de la frecuencia de los números del 0 al 31 (en muestras de 1M):

ingrese la descripción de la imagen aquí

Floris
fuente
nitpick: esto alienta números muy pequeños más de lo que uno podría esperar. La probabilidad de obtener un cero es significativamente mayor que un 10.
Mooing Duck
Bueno, el objetivo de esto es alentar números pequeños, ¡así que me alegro de que funcione! Ejecuté una simulación de Monte Carlo, y esto me está dando una caída del factor 2 en la probabilidad de que los números se dupliquen, no muy diferente de una distribución de registros. Respuesta actualizada con una foto.
Floris
no, quiero decir, con rand()>>(rand()&31);, uno intuitivamente esperaría que 1/32 de los números tengan 32 bits, y 1/32 de los números tengan 31 bits, y 1/32 de los números tengan 30 bits, etc. Pero eso es no los resultados que está obteniendo, solo aproximadamente 1/64 de los números darían como resultado 32 bits, mientras que casi la mitad debería ser 0. Dado que mis cálculos mentales no están de acuerdo con sus mediciones, tendré que hacer mis propias mediciones para calcular esto afuera.
Mooing Duck
2
No quiero decir que tu código esté equivocado. Probablemente sea lo que haría. Simplemente merece una advertencia de que los resultados no se distribuyen del todo como cabría esperar.
Mooing Duck
1
Creo que el problema proviene de pensar en 0 como un número de 1 bit ... ese es el tipo de enigma con el que te encuentras cuando mezclas enteros y logaritmos. Sin embargo, ha sido un buen ejercicio y me diste algo en qué pensar. "Prueba los límites de tu algoritmo": nunca pasa de moda.
Floris
5

Hay exactamente el mismo número de números entre 0 y 2 ^ 29 y 2 ^ 29 y 2 ^ 30.

Otra forma de ver el problema: considere la representación binaria del número aleatorio que genera, la probabilidad de que el bit más alto sea 1 es igual a 1/2 y, por lo tanto, obtiene el orden 29 en la mitad de los casos. Lo que quiere es ver un número que esté por debajo de 2 ^ 25, pero eso significa que 5 bits más altos son todos cero, lo que ocurre con una baja probabilidad de 1/32. Lo más probable es que incluso si lo ejecutas durante mucho tiempo nunca verás el orden por debajo de 15 (la probabilidad es algo así como tirar 6 6 veces seguidas).

Ahora, la parte de tu pregunta sobre la semilla. No, la semilla no puede determinar el rango desde el que se generan los números, solo determina el primer elemento inicial. Piense en rand () como una secuencia de todos los números posibles en el rango (permutación predeterminada). La semilla determina dónde comienza a dibujar números de la secuencia. Es por eso que si desea (pseudo) aleatoriedad, usa el tiempo actual para inicializar la secuencia: no le importa que la posición desde la que comienza no esté distribuida uniformemente, lo único que importa es que nunca comience desde la misma posición.

Vadim
fuente
2

¡Úselo pow(2,rand()) le dará las respuestas en orden de magnitud deseada!

Shivendra
fuente
2

Si desea usar números aleatorios de un servicio en línea, puede usar wget para eso, es posible que desee ver que también puede usar servicios como random.org para su generación de números aleatorios, puede atraparlos usando wget y luego leer los números de el archivo descargado

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html

Namit Sinha
fuente
Bienvenido a SO. absténgase de publicar enlaces como respuestas. Puede proporcionar un boceto detallado de una respuesta dejando que los detalles se lean a través de enlaces.
Shai