Estoy tratando de hacer un juego con dados, y necesito tener números aleatorios (para simular los lados del dado. Sé cómo hacerlo entre 1 y 6). Utilizando
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
int main()
{
srand((unsigned)time(0));
int i;
i = (rand()%6)+1;
cout << i << "\n";
}
no funciona muy bien, porque cuando ejecuto el programa varias veces, aquí está el resultado que obtengo:
6
1
1
1
1
1
2
2
2
2
5
2
Entonces, quiero un comando que genere un número aleatorio diferente cada vez, no el mismo 5 veces seguidas. ¿Hay un comando que haga esto?
Respuestas:
El problema más fundamental de su aplicación de prueba es que llama
srand
una vez y luego llamarand
una vez y sale.El objetivo de la
srand
función es inicializar la secuencia de números pseudoaleatorios con una semilla aleatoria.Significa que si pasa el mismo valor a
srand
dos aplicaciones diferentes (con la mismasrand
/rand
implementación) , obtendrá exactamente la misma secuencia derand()
valores leídos después de eso en ambas aplicaciones.Sin embargo, en su aplicación de ejemplo, la secuencia pseudoaleatoria consta solo de un elemento: el primer elemento de una secuencia pseudoaleatoria generada a partir de la inicialización igual al tiempo actual de
second
precisión. ¿Qué esperas ver en la salida entonces?Obviamente, cuando ejecuta la aplicación en el mismo segundo, usa el mismo valor inicial, por lo tanto, su resultado es el mismo (como Martin York ya mencionó en un comentario a la pregunta).
En realidad, debe llamar
srand(seed)
una vez y luego llamarrand()
muchas veces y analizar esa secuencia; debe verse al azar.EDITAR:
Oh ya entiendo. Aparentemente, la descripción verbal no es suficiente (tal vez una barrera del idioma o algo así ... :)).
OKAY. Ejemplo de código C antiguo basado en las mismas
srand()/rand()/time()
funciones que se usaron en la pregunta:^^^ QUE secuencia de una sola ejecución del programa se supone que se vea al azar.
EDIT2:
Cuando se usa la biblioteca estándar C o C ++, es importante comprender que, a partir de ahora, no hay una sola función estándar o clase que produzca datos realmente aleatorios definitivamente (garantizado por el estándar). La única herramienta estándar que aborda este problema es std :: random_device que desafortunadamente aún no ofrece garantías de aleatoriedad real.
Dependiendo de la naturaleza de la aplicación, primero debe decidir si realmente necesita datos verdaderamente aleatorios (impredecibles). Un caso notable cuando realmente necesita una aleatoriedad verdadera es la seguridad de la información, por ejemplo, generar claves simétricas, claves privadas asimétricas, valores de sal, tokens de seguridad, etc.
Sin embargo, los números aleatorios de grado de seguridad son una industria separada que vale un artículo separado.
En la mayoría de los casos , el generador de números pseudoaleatorios es suficiente, por ejemplo, para simulaciones científicas o juegos. En algunos casos, incluso se requiere una secuencia pseudoaleatoria definida de manera consistente, por ejemplo, en los juegos puedes elegir generar exactamente los mismos mapas en tiempo de ejecución para evitar almacenar muchos datos.
La pregunta original y la multitud recurrente de preguntas idénticas / similares (e incluso muchas "respuestas" equivocadas) indican que, ante todo, es importante distinguir los números aleatorios de los números pseudoaleatorios Y comprender qué es una secuencia de números pseudoaleatoria en el primer lugar Y para darse cuenta de que los generadores de números pseudoaleatorios NO se usan de la misma manera que podría usar generadores de números aleatorios verdaderos.
^^^ ESE tipo de expectativas intuitivas es MUY INCORRECTO y dañino en todos los casos que involucran generadores de números pseudoaleatorios, a pesar de ser razonables para números aleatorios verdaderos.
Si bien existe la noción significativa de "número aleatorio", no existe tal cosa como "número pseudoaleatorio". Un generador de números pseudoaleatorios en realidad produce una secuencia de números pseudoaleatorios .
Cuando los expertos hablan de la calidad de PRNG, en realidad hablan de las propiedades estadísticas de la secuencia generada (y sus notables subsecuencias). Por ejemplo, si combina dos PRNG de alta calidad usándolos ambos por turnos, puede producir una secuencia resultante mala, a pesar de que generan secuencias buenas cada una por separado (esas dos secuencias buenas pueden simplemente correlacionarse entre sí y, por lo tanto, combinarse mal).
De hecho, la secuencia pseudoaleatoria siempre es determinista (predeterminada por su algoritmo y parámetros iniciales), es decir, en realidad no tiene nada de aleatorio.
Específicamente
rand()
/srand(s)
par de funciones proporciona una por proceso singular no thread-safe (!) Secuencia de números pseudo-aleatorio generado con el algoritmo definido por la implementación. La funciónrand()
produce valores en rango[0, RAND_MAX]
.Cita del estándar C11:
Mucha gente espera razonablemente que eso
rand()
produzca una secuencia de números semi-independientes distribuidos uniformemente en el rango0
deRAND_MAX
. Bueno, definitivamente debería (de lo contrario es inútil), pero desafortunadamente no solo el estándar no requiere eso, incluso hay un descargo de responsabilidad explícito que establece que "no hay garantías en cuanto a la calidad de la secuencia aleatoria producida" . En algunos casos históricosrand
/srand
aplicación era de muy mala calidad por cierto. Aunque en las implementaciones modernas es probable que sea lo suficientemente bueno, pero la confianza está rota y no es fácil de recuperar. Además, su naturaleza no segura para subprocesos hace que su uso seguro en aplicaciones de subprocesos múltiples sea complicado y limitado (aún posible, puede usarlos desde un subproceso dedicado).La nueva plantilla de clase std :: mersenne_twister_engine <> (y su conveniencia typedefs -
std::mt19937
/std::mt19937_64
con una buena combinación de parámetros de plantilla) proporciona un generador de números pseudoaleatorio por objeto definido en el estándar C ++ 11. Con los mismos parámetros de plantilla y los mismos parámetros de inicialización, diferentes objetos generarán exactamente la misma secuencia de salida por objeto en cualquier computadora en cualquier aplicación creada con una biblioteca estándar compatible con C ++ 11. La ventaja de esta clase es su secuencia de salida predecible de alta calidad y su consistencia total en todas las implementaciones.También hay más motores PRNG definidos en el estándar C ++ 11: std :: linear_congruential_engine <> (utilizado históricamente como
srand/rand
algoritmo de calidad razonable en algunas implementaciones de biblioteca estándar C) y std :: subtract_with_carry_engine <> . También generan secuencias de salida por objeto dependientes de parámetros completamente definidas.Reemplazo de ejemplo de C ++ 11 moderno para el código C obsoleto anterior:
La versión del código anterior que usa std :: uniform_int_distribution <>
fuente
rand()
ysrand()
. ¿Puedes actualizarlo?rand()
ysrand()
. De hecho, solo responde la pregunta con la descripción proporcionada. Es evidente por la descripción (que usarand
/srand
) que los conceptos básicos de generación de números pseudoaleatorios deben explicarse, como el significado mismo de la secuencia pseudoaleatoria y su semilla. Estoy tratando de hacer exactamente eso y utilizar el más simple y familiarrand
/srand
combinación. Lo curioso es que algunas otras respuestas, incluso con una calificación muy alta, sufren los mismos malentendidos que el autor de la pregunta.std::rand/std::srand
características y nuevos C ++ biblioteca comostd::random_device<>
, std :: mersenne_twister_engine <> y multitud de distribuciones aleatorias requieren alguna explicación.El uso de módulo puede introducir sesgo en los números aleatorios, dependiendo del generador de números aleatorios. Vea esta pregunta para más información. Por supuesto, es perfectamente posible obtener números repetidos en una secuencia aleatoria.
Pruebe algunas características de C ++ 11 para una mejor distribución:
Consulte esta pregunta / respuesta para obtener más información sobre los números aleatorios de C ++ 11. Lo anterior no es la única forma de hacer esto, pero es una forma.
fuente
%6
es muy pequeña. Quizás sea significativo si estás escribiendo un juego de dados para ser usado en Las Vegas, pero no tiene consecuencias en casi cualquier otro contexto.random_device
ymt19937
ya no existe, literalmente, ninguna razón para no utilizar todo el estándaruniform_int_distribution
.Si está utilizando libs boost , puede obtener un generador aleatorio de esta manera:
Donde la función
current_time_nanoseconds()
proporciona el tiempo actual en nanosegundos que se utiliza como semilla.Aquí hay una clase más general para obtener enteros aleatorios y fechas en un rango:
fuente
http://en.cppreference.com/w/cpp/numeric/random/rand
fuente
%6
). Y si decidió usar lastd::rand
API derand
C ++ de la función de biblioteca C, ¿por qué no usarstd::time
ystd::srand
en aras de la coherencia de estilo C ++?¡Puede obtener un
Randomer
código de clase completo para generar números aleatorios desde aquí!Si necesita números aleatorios en diferentes partes del proyecto, puede crear una clase separada
Randomer
para encapsular todo lo que contienerandom
.Algo como eso:
Tal clase sería útil más adelante:
Puede consultar este enlace como ejemplo de cómo uso dicha
Randomer
clase para generar cadenas aleatorias. También puede usarRandomer
si lo desea.fuente
Escenario de caso de uso
Comparé el problema de la previsibilidad con una bolsa de seis bits de papel, cada uno con un valor de 0 a 5 escrito en él. Se saca un pedazo de papel de la bolsa cada vez que se requiere un nuevo valor. Si la bolsa está vacía, los números se vuelven a poner en la bolsa.
... a partir de esto, puedo crear una especie de algoritmo.
Algoritmo
Una bolsa suele ser a
Collection
. Elegí unbool[]
(también conocido como matriz booleana, plano de bits o mapa de bits) para asumir el papel de la bolsa.La razón por la que elegí a
bool[]
es porque el índice de cada elemento ya es el valor de cada hoja de papel. Si los documentos requirieran algo más escrito en ellos, entonces habría usado unDictionary<string, bool>
en su lugar. El valor booleano se usa para realizar un seguimiento de si el número se ha extraído o no.Se
RemainingNumberCount
inicializa un contador llamado5
que cuenta atrás a medida que se elige un número aleatorio. Esto nos evita tener que contar cuántos trozos de papel quedan cada vez que deseamos dibujar un nuevo número.Para seleccionar el siguiente valor aleatorio, estoy usando un
for..loop
para escanear la bolsa de índices y un contador para contar cuandoindex
sefalse
llama aNumberOfMoves
.NumberOfMoves
se usa para elegir el siguiente número disponible.NumberOfMoves
se establece primero como un valor aleatorio entre0
y5
, porque hay 0..5 pasos disponibles que podemos realizar a través de la bolsa. En la próxima iteraciónNumberOfMoves
se establece un valor aleatorio entre0
y4
, porque ahora hay 0..4 pasos que podemos hacer a través de la bolsa. A medida que se usan los números, los números disponibles se reducen, por lo que en su lugar los usamosrand() % (RemainingNumberCount + 1)
para calcular el siguiente valor deNumberOfMoves
.Cuando el
NumberOfMoves
contador llega a cero,for..loop
debería ser el siguiente:for..loop
el índice de.false
.for..loop
.Código
El código para la solución anterior es el siguiente:
(coloque los siguientes tres bloques en el archivo principal .cpp uno tras otro)
Una clase de consola
Creo esta clase de consola porque hace que sea fácil redirigir la salida.
Abajo en el código ...
...puede ser reemplazado por...
... y luego esta
Console
clase se puede eliminar si lo desea.Método principal
Ejemplo de uso de la siguiente manera:
Salida de ejemplo
Cuando ejecuté el programa, obtuve el siguiente resultado:
Frase de cierre
Este programa fue escrito usando Visual Studio 2017 , y elegí convertirlo en un
Visual C++ Windows Console Application
proyecto usando.Net 4.6.1
.No estoy haciendo nada particularmente especial aquí, por lo que el código también debería funcionar en versiones anteriores de Visual Studio.
fuente
Siempre que realice una búsqueda web básica
random number generation
en el lenguaje de programación C ++, esta pregunta suele ser la primera en aparecer. ¡Quiero lanzar mi sombrero al ring para poder aclarar mejor el concepto de generación de números pseudoaleatorios en C ++ para futuros codificadores que inevitablemente buscarán esta misma pregunta en la web!Los basicos
La generación de números pseudoaleatorios implica el proceso de utilizar un algoritmo determinista que produce una secuencia de números cuyas propiedades se parecen aproximadamente a números aleatorios . Digo que se parecen aproximadamente , porque la verdadera aleatoriedad es un misterio bastante difícil de alcanzar en matemáticas y ciencias de la computación. Por lo tanto, ¡por qué el término pseudoaleatorio se utiliza para ser más pedante correcto!
Antes de poder usar un PRNG, es decir,
pseudo-random number generator
debe proporcionar al algoritmo un valor inicial que a menudo se denomina semilla . Sin embargo, ¡la semilla solo debe establecerse una vez antes de usar el algoritmo mismo!Por lo tanto, si desea una buena secuencia de números, ¡debe proporcionar una semilla amplia al PRNG!
El viejo camino de C
La biblioteca estándar de C compatible con versiones anteriores que tiene C ++, utiliza lo que se llama un generador congruencial lineal que se encuentra en el
cstdlib
archivo de encabezado. Este PRNG funciona a través de una función discontinua discontinua que utiliza aritmética modular, es decir, un algoritmo rápido que le gusta usarmodulo operator '%'
. El siguiente es el uso común de este PRNG, con respecto a la pregunta original hecha por @Predictability:El uso común de PRNG de C alberga una gran cantidad de problemas como:
std::rand()
no es muy intuitiva para la generación adecuada de números pseudoaleatorios entre un rango dado, por ejemplo, producir números entre [1, 6] de la manera que quería @Predictability.std::rand()
elimina la posibilidad de una distribución uniforme de números pseudoaleatorios, debido al Principio de Pigeonhole .std::rand()
sembradastd::srand( ( unsigned int )std::time( nullptr ) )
técnicamente no es correcta, porquetime_t
se considera un tipo restringido . Por lo tanto, la conversión detime_t
aunsigned int
no está garantizada.Para obtener información más detallada sobre los problemas generales del uso de PRNG de C y cómo evitarlos, consulte Uso de rand () (C / C ++): ¡Consejos para la función rand () de la biblioteca estándar de C !
La forma estándar de C ++
Desde que se publicó el estándar ISO / IEC 14882: 2011, es decir, C ++ 11, la
random
biblioteca ha estado separada del lenguaje de programación C ++ por un tiempo. Esta biblioteca está equipada con múltiples PRNG, y diferentes tipos de distribución , tales como: distribución uniforme , distribución normal , distribución binomial , etc. El siguiente ejemplo de código fuente demuestra un uso muy básico de larandom
biblioteca, en lo que respecta a la pregunta original de @ Previsibilidad:El motor Mersenne Twister de 32 bits , con una distribución uniforme de valores enteros , se utilizó en el ejemplo anterior. (El nombre del motor en el código fuente suena extraño, porque su nombre proviene de su período de 2 ^ 19937-1). El ejemplo también se utiliza
std::random_device
para inicializar el motor, que obtiene su valor del sistema operativo (si está utilizando un sistema Linux,std::random_device
devuelve un valor de/dev/urandom
).Tenga en cuenta que no tiene que usar
std::random_device
para sembrar ningún motor . ¡Puedes usar constantes o incluso lachrono
biblioteca! Tampoco tiene que usar la versión de 32 bits delstd::mt19937
motor, ¡hay otras opciones ! Para obtener más información sobre las capacidades de larandom
biblioteca, consulte cplusplus.comEn general, los programadores de C ++ ya no deberían usarlo
std::rand()
, no porque sea malo , sino porque el estándar actual ofrece mejores alternativas que son más directas y confiables . Con suerte, muchos de ustedes encuentran esto útil, ¡especialmente aquellos de ustedes que recientemente buscaron en la webgenerating random numbers in c++
!fuente
Aquí hay una solución. Cree una función que devuelva el número aleatorio y colóquelo fuera de la función principal para que sea global. Espero que esto ayude
fuente
Este código produce números aleatorios desde
n
hastam
.ejemplo:
fuente
srand(time(0))
a la función principal antesrandom(n, m)
?srand(time(0))
a la función principal no a for loop o dentro de la implementación de la función.para aleatorio cada archivo RUN
fuente
Aquí hay un generador aleatorio simple con aprox. igual probabilidad de generar valores positivos y negativos alrededor de 0:
fuente