<random>
Define instalaciones para generar números aleatorios, lo que permite crear números aleatorios distribuidos de manera uniforme.
#include <random>
Resumen
Un generador de números aleatorios es un objeto que crea una secuencia de valores seudoaleatorios. Un generador que crea valores distribuidos uniformemente en un rango especificado es un generador de números aleatorios uniformes (URNG). La clase de plantilla diseñada para actuar como URNG se conoce como motor si tal clase posee determinados rasgos comunes (esto se abordará más adelante en este artículo). Un URNG se puede combinar (de hecho, suele combinarse) con una distribución si el URNG se pasa como un argumento al operator() de la distribución con el fin de generar valores que se distribuyen de la forma especificada por la distribución.
Estos vínculos llevan a las secciones principales de este artículo:
Ejemplos de código
Lista por categorías
Motores y distribuciones
Comentarios
Sugerencias
Aquí incluimos algunas sugerencias que conviene recordar al usar <random>:
En la mayoría de los casos, los URNG generan bits sin formato a los que las distribuciones deben dar forma. (una excepción significativa a este respecto es std::shuffle(), ya que usa un URNG directamente).
No se puede llamar a una única creación de instancias de un URNG o distribución simultáneamente de forma segura, ya que la ejecución de un URNG o distribución es una operación de modificación. Para obtener más información, consulta Seguridad para subprocesos en la biblioteca estándar de C++.
Se ofrecen typedefs predefinidos de varios motores; esta es la forma preferida de crear un URNG si se usa un motor.
El emparejamiento más útil en la mayoría de los casos es la del motor mt19937 con uniform_int_distribution, tal y como demuestra el código de ejemplo que aparece más adelante en este artículo.
Son muchas las opciones que se pueden elegir en el encabezado <random>, y todas ellas son preferibles a la función de tiempo de ejecución C obsoleta rand(). Para más información sobre los aspectos negativos de rand() y ver cómo <random> los aborda, vea este vídeo.
Ejemplos
En el ejemplo de código siguiente, se muestra cómo generar algunos números aleatorios (en este caso, cinco) con un generador creado con valores de inicialización no deterministas.
#include <random>
#include <iostream>
using namespace std;
int main()
{
random_device rd; // non-deterministic generator
mt19937 gen(rd()); // to seed mersenne twister.
// replace the call to rd() with a
// constant value to get repeatable
// results.
for (int i = 0; i < 5; ++i) {
cout << gen() << " "; // print the raw output of the generator.
}
cout << endl;
}
Resultado:
Aunque son números aleatorios de gran calidad y diferentes cada vez que se ejecuta este programa, no están incluidos necesariamente en un intervalo útil. Para controlar el intervalo, utilice una distribución uniforme, como se muestra en el siguiente código:
#include <random>
#include <iostream>
using namespace std;
int main()
{
random_device rd; // non-deterministic generator
mt19937 gen(rd()); // to seed mersenne twister.
uniform_int_distribution<> dist(1,6); // distribute results between 1 and 6 inclusive.
for (int i = 0; i < 5; ++i) {
cout << dist(gen) << " "; // pass the generator to the distribution.
}
cout << endl;
}
Resultado:
En el ejemplo de código siguiente, se muestra un conjunto más realista de casos de uso con generadores de números aleatorios distribuidos uniformemente que seleccionan aleatoriamente el contenido de un vector y una matriz.
// cl.exe /EHsc /nologo /W4 /MTd
#include <algorithm>
#include <array>
#include <iostream>
#include <random>
#include <string>
#include <vector>
#include <functional> // ref()
using namespace std;
template <typename C> void print(const C& c) {
for (const auto& e : c) {
cout << e << " ";
}
cout << endl;
}
template <class URNG>
void test(URNG& urng) {
// Uniform distribution used with a vector
// Distribution is [-5, 5] inclusive
uniform_int_distribution<int> dist(-5, 5);
vector<int> v;
for (int i = 0; i < 20; ++i) {
v.push_back(dist(urng));
}
cout << "Randomized vector: ";
print(v);
// Shuffle an array
// (Notice that shuffle() takes a URNG, not a distribution)
array<string, 26> arr = { { "H", "He", "Li", "Be", "B", "C", "N", "O", "F",
"Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc",
"Ti", "V", "Cr", "Mn", "Fe" } };
shuffle(arr.begin(), arr.end(), urng);
cout << "Randomized array: ";
print(arr);
cout << "--" << endl;
}
int main()
{
// First run: non-seedable, non-deterministic URNG random_device
// Slower but crypto-secure and non-repeatable.
random_device rd;
cout << "Using random_device URNG:" << endl;
test(rd);
// Second run: simple integer seed, repeatable results
cout << "Using constant-seed mersenne twister URNG:" << endl;
mt19937 engine1(12345);
test(engine1);
// Third run: random_device as a seed, different each run
// (Desirable for most purposes)
cout << "Using non-deterministic-seed mersenne twister URNG:" << endl;
mt19937 engine2(rd());
test(engine2);
// Fourth run: "warm-up" sequence as a seed, different each run
// (Advanced uses, allows more than 32 bits of randomness)
cout << "Using non-deterministic-seed \"warm-up\" sequence mersenne twister URNG:" << endl;
array<unsigned int, mt19937::state_size> seed_data;
generate_n(seed_data.begin(), seed_data.size(), ref(rd));
seed_seq seq(begin(seed_data), end(seed_data));
mt19937 engine3(seq);
test(engine3);
}
Resultado del ejemplo y comentarios sobre el código
En este código se muestran dos aleatorizaciones distintas (la aleatorización de un vector de enteros y la selección aleatoria de una matriz de datos indexados) con una función de plantilla de prueba. En la primera llamada a la función de prueba usa el URNG no reiterativo, no propagable, no determinista y criptográficamente seguro random_device. En la segunda serie de pruebas se usa mersenne_twister_engine como URNG con un valor de inicialización constante de 32 bits, lo que significa que los resultados son reiterativos. En la tercera serie de pruebas se propaga mersenne_twister_engine con un resultado no determinista de 32 bits de random_device. En la cuarta serie de pruebas esto se expande por medio de una secuencia de propagación rellena con resultados de random_device, con lo que se consigue eficazmente una aleatoriedad no determinista de más de 32 bits (si bien sigue sin ser criptográficamente seguro). Siga leyendo para obtener más información.
[ir a la parte superior de la página]
Lista por categorías
Generadores de números aleatorios uniformes
A menudo, los URNG se describen según los siguientes aspectos:
Longitud del período: número de iteraciones necesarias para repetir la secuencia de números generada. Cuanto más largo, mejor.
Rendimiento: rapidez con la que se pueden generar los números y cantidad de memoria que requiere. Cuanto menos, mejor.
Calidad: cercanía de la secuencia generada a los números aleatorios auténticos. Esto se suele conocer como "aleatoriedad".
En las siguientes secciones se enumeran los generadores de números aleatorios que se proporcionan en el encabezado <random>.
[ir a la parte superior de la página]
Generador no determinista
Genera una secuencia aleatoria criptográficamente segura y no determinista mediante un dispositivo externo. Se suele usar para propagar un motor. Bajo rendimiento y calidad extremadamente alta. Para obtener más información, vea Comentarios. |
[ir a la parte superior de la página]
Typedefs de motor con parámetros predefinidos
Para crear instancias de motores y adaptadores de motores. Para obtener más información, vea Motores y distribuciones.
Name |
Descripción |
---|---|
default_random_engine |
Definición de tipo del motor predeterminado.
|
knuth_b |
Motor de Knuth.
|
minstd_rand0 |
Motor estándar mínimo 1988 (Lewis, Goodman y Miller, 1969).
|
minstd_rand |
Motor estándar mínimo minstd_rand0 actualizado (Park, Miller y Stockmeyer, 1993).
|
mt19937 |
Motor Mersenne Twister de 32 bits (Matsumoto y Nishimura, 1998).
|
mt19937_64 |
Motor Mersenne Twister de 64 bits (Matsumoto y Nishimura, 2000).
|
ranlux24 |
Motor RANLUX de 24 bits (Martin Lüscher y Fred James, 1994).
|
ranlux24_base |
Usado como base de ranlux24.
|
ranlux48 |
Motor RANLUX de 48 bits (Martin Lüscher y Fred James, 1994).
|
ranlux48_base |
Usado como base de ranlux48.
|
[ir a la parte superior de la página]
Plantillas de motor
Las plantillas de motor se usan como URNG independientes o como motores base que se pasan a los adaptadores de motor. Por lo general, se suelen crear instancias de ellas con un typedef de motor predefinido y pasarse a una distribución. Para más información, vea la sección Motores y distribuciones.
Genera una secuencia aleatoria mediante un algoritmo congruencial lineal. Es la de mayor simpleza y calidad más baja. |
|
Genera una secuencia aleatoria mediante un algoritmo Mersenne Twister. La más compleja y de mayor calidad, salvo en el caso de la clase random_device. Rendimiento muy rápido. |
|
Genera una secuencia aleatoria mediante un algoritmo de resta llevando. Constituye una mejora con respecto a linear_congruential_engine, si bien la calidad y rendimiento son muy inferiores a mersenne_twister_engine. |
[ir a la parte superior de la página]
Plantillas de adaptador de motor
Los adaptadores de motor son plantillas que adaptan otros motores (base). Por lo general, se suelen crear instancias de ellas con un typedef de motor predefinido y pasarse a una distribución. Para más información, vea la sección Motores y distribuciones.
Genera una secuencia aleatoria descartando los valores que devuelve su motor base. |
|
Genera una secuencia aleatoria con un número específico de bits, para lo cual vuelve a empaquetar los bits de los valores que devuelve su motor base. |
|
Genera una secuencia aleatoria reordenando los valores que devuelve su motor base. |
[ir a la parte superior de la sección]
[ir a la parte superior de la página]
Distribuciones de números aleatorios
En las siguientes secciones se enumeran las distribuciones que se proporcionan en el encabezado <random>. Las distribuciones son un mecanismo de procesamiento posterior que suelen usar una salida de URNG como entrada y distribuir esa salida mediante una función de densidad de probabilidad estadística definida. Para más información, vea la sección Motores y distribuciones.
[ir a la parte superior de la página]
Distribuciones uniformes
Genera una distribución uniforme de valores enteros por un rango en el intervalo cerrado [a, b] (inclusivo-inclusivo). |
|
Genera una distribución uniforme de valores reales (punto flotante) por un rango en el intervalo [a, b] (inclusivo-exclusivo). |
|
Genera una distribución uniforme de valores reales (punto flotante) de una determinada precisión a lo largo de [0, 1] (inclusivo-exclusivo). |
[ir a la parte superior de la sección]
Distribuciones de Bernoulli
Genera una distribución de Bernoulli de valores bool. |
|
Genera una distribución binomial de valores enteros. |
|
Genera una distribución geométrica de valores enteros. |
|
Genera una distribución binomial negativa de valores enteros. |
[ir a la parte superior de la sección]
Distribuciones normales
Genera una distribución de Cauchy de valores reales (punto flotante). |
|
Genera una distribución chi-cuadrado de valores reales (punto flotante). |
|
Genera una distribución F (también llamada “distribución F de Snedecor” o “distribución de Fisher-Snedecor”) de valores reales (punto flotante). |
|
Genera una distribución log-normal de valores reales (punto flotante). |
|
Genera una distribución normal (Gaussiana) de valores reales (punto flotante). |
|
Genera una distribución t de Student de valores reales (punto flotante). |
[ir a la parte superior de la sección]
Distribuciones de Poisson
Genera una distribución de exponencial de valores reales (punto flotante). |
|
Genera una distribución de valores extremos de valores reales (punto flotante). |
|
Genera una distribución gamma de valores reales (punto flotante). |
|
Genera una distribución de Poisson de valores enteros. |
|
Genera una distribución de Weibull de valores reales (punto flotante). |
[ir a la parte superior de la sección]
Distribuciones de muestreo
Genera una distribución discreta de enteros. |
|
Genera una distribución constante a trozos de valores reales (punto flotante). |
|
Genera una distribución lineal a trozos de valores reales (punto flotante). |
[ir a la parte superior de la sección]
[ir a la parte superior de la página]
Funciones de utilidad
En esta sección se enumeran las funciones de utilidad generales que se proporcionan en el encabezado <random>.
Genera una secuencia de propagación codificada no inclinada. Sirve para evitar la replicación de flujos variados aleatorios. Resulta práctica cuando se crean instancias de muchos URNG desde los motores. |
Operadores
En esta sección se enumeran los operadores que se proporcionan en el encabezado <random>.
operator== |
Comprueba si el URNG en el lado izquierdo del operador es igual al motor en el lado derecho. |
operator!= |
Comprueba si el URNG en el lado izquierdo del operador no es igual al motor en el lado derecho. |
operator<< |
Escribe información de estado en un flujo. |
operator>> |
Extrae información de estado de un flujo. |
[ir a la parte superior de la página]
Motores y distribuciones
Consulte las siguientes secciones para obtener información sobre cada una de las categorías de clase de plantilla definidas en <random>. Estas dos categorías de clase de plantilla toman un tipo como argumento y usan nombres compartidos de parámetros de plantilla para describir las propiedades del tipo que son posibles como tipo de argumento real, como se indica a continuación:
IntType indica short, int, long, long long, unsigned short, unsigned int, unsigned long o unsigned long long.
UIntType indica unsigned short, unsigned int, unsigned long o unsigned long long.
RealType indica float, double o long double.
Motores
Los Motores y los adaptadores de motor son plantillas cuyos parámetros sirven para personalizar el generador creado.
Un motor es una clase o clase de plantilla cuyas instancias (generadores) actúan como origen de números aleatorios distribuidos uniformemente entre un valor máximo y mínimo. Un adaptador de motor proporciona una secuencia de valores con distintas propiedades de aleatoriedad, para lo cual toma los valores generados por cualquier otro motor de números aleatorios y aplica a algunos de ellos un algoritmo de un tipo determinado.
Todos los motores y adaptadores de motor tienen los siguientes miembros:
typedef numeric-type result_type es el tipo que el operator() del generador devuelve. El numeric-type se pasa como un parámetro de plantilla al crear las instancias.
result_type operator() devuelve valores que se distribuyen de manera uniforme entre min() y max().
result_type min() devuelve el valor mínimo que el operator() del generador devuelve. Los adaptadores de motor usan el resultado min() del motor base.
result_type max() devuelve el valor máximo que el operator() del generador devuelve. Cuando result_type es un tipo integral (con valor entero), max() es el valor máximo (inclusive) que se puede devolver realmente y, cuando result_type es un tipo de punto flotante (con valor real), max() es el valor más pequeño (ni inclusive) superior a todos los valores que se pueden devolver. Los adaptadores de motor usan el resultado max() del motor base.
void seed(result_type s) propaga el generador con el valor de inicialización s. En los motores, la signatura es void seed(result_type s = default_seed) para la compatibilidad de parámetros predeterminada (los adaptadores de motor definen otro void seed(), consulte la siguiente subsección).
template <class Seq> void seed(Seq& q) propaga el generador mediante una seed_seq Seq.
Un constructor explícito con el argumento result_type x que crea un generador propagado como si se llamara a seed(x).
Un constructor explícito con el argumento seed_seq& seq que crea un generador propagado como si se llamara a seed(seq).
void discard(unsigned long long count) llama eficazmente a operator() count veces y descarta cada valor.
Los adaptadores de motor admiten también estos miembros (Engine es el primer parámetro de plantilla de un adaptador de motor y designa el tipo del motor base):
Un constructor predeterminado para inicializar el generador como si se hiciera desde el constructor predeterminado del motor base.
Un constructor explícito con el argumento const Engine& eng. Sirve para admitir una construcción de copia mediante el motor base.
Un constructor explícito con el argumento Engine&& eng. Sirve para admitir una construcción de movimiento mediante el motor base.
void seed() que inicializa el generador con el valor de inicialización predeterminado del motor base.
Función de propiedad const Engine& base() que devuelve el motor base que se usó para construir el generador.
Cada motor mantiene un estado que indica la secuencia de valores que se va a generar a partir de las subsiguientes llamadas a operator(). Los estados de dos generadores de los que se crearon instancias desde motores del mismo tipo se pueden comparar mediante operator== y operator!=. Si dos estados comparados son iguales, generarán la misma secuencia de valores. El estado de un objeto se puede guardar en un flujo como una secuencia de valores sin signo de 32 bits mediante el operator<< del generador. El estado no se cambia al guardarlo. Un estado guardado se puede leer en el generador del que se creó la instancia desde un motor del mismo tipo mediante operator>>.
[ir a la parte superior de la página]
Distribuciones
Una distribución es una clase o una clase de plantilla cuyas instancias transforman un flujo de números aleatorios distribuidos uniformemente obtenidos de un motor en un flujo de números aleatorios que presenta una distribución particular. Todas las distribuciones tienen los siguientes miembros:
typedef numeric-type result_type es el tipo que el operator() de la distribución devuelve. El numeric-type se pasa como un parámetro de plantilla al crear las instancias.
template <class URNG> result_type operator()(URNG& gen) devuelve valores que se distribuyen según la definición de la distribución, para lo cual se usa gen como origen de los valores aleatorios distribuidos uniformemente y los parámetros de la distribución almacenados.
template <class URNG> result_type operator()(URNG& gen, param_type p) devuelve valores que se distribuyen según la definición de la distribución, para lo cual se usa gen como origen de los valores aleatorios distribuidos uniformemente y la estructura de parámetros p.
typedef unspecified-type param_type es el paquete de parámetros que se pasa opcionalmente a operator() y que se usa en lugar de los parámetros almacenados para generar su valor de retorno.
Un constructor const param& inicializa los parámetros almacenados desde su argumento.
param_type param() const obtiene los parámetros almacenados.
void param(const param_type&) establece los parámetros almacenados desde su argumento.
result_type min() devuelve el valor mínimo que el operator() de la distribución devuelve.
result_type max() devuelve el valor máximo que el operator() de la distribución devuelve. Cuando result_type es un tipo integral (con valor entero), max() es el valor máximo (inclusive) que se puede devolver realmente y, cuando result_type es un tipo de punto flotante (con valor real), max() es el valor más pequeño (ni inclusive) superior a todos los valores que se pueden devolver.
void reset() descarta cualquier valor almacenado en caché, de modo que la siguiente llamada a operator() no depende de ningún valor obtenido del motor antes de la llamada.
Una estructura de parámetros es un objeto donde se almacenan todos los parámetros necesarios para una distribución. Contiene lo siguiente:
typedef distribution-type distribution_type, que es el tipo de su distribución correspondiente.
Uno o más constructores que toman las mismas listas de parámetros que los constructores de la distribución.
Las mismas funciones de acceso a parámetros que la distribución.
Operadores de comparación de igualdad y desigualdad.
Para más información, vea los subtemas de referencia cuyos vínculos aparecen recogidos anteriormente en este artículo.
[ir a la parte superior de la página]
Comentarios
Visual Studio dispone de dos URNG de extrema utilidad: mt19937 y random_device, comparados en la siguiente tabla:
URNG |
¿Rápido? |
¿Seguro criptográficamente? |
¿Propagable? |
¿Determinista? |
---|---|---|---|---|
mt19937 |
Sí |
No |
Sí |
Sí* |
random_device |
No |
Sí |
No |
No |
* Cuando se proporciona con un valor de inicialización conocido.
En Visual Studio, random_device se implementa de forma que es seguro criptográficamente, aunque la norma ISO C++ no lo requiera. (El término “seguro criptográficamente” no implica ninguna garantía, sino que hace referencia al nivel mínimo de entropía —y, por lo tanto, el grado de posibilidad de predicción— que proporciona un algoritmo de aleatorización. Para más información, consulte el artículo de la Wikipedia sobre el generador de números seudoaleatorios seguros criptográficamente). Dado que la norma ISO C++ no requiere esto, puede que otras plataformas implementen random_device como un generador de números seudoaleatorios simple (no seguro criptográficamente) que solo sea adecuado como origen de valores de inicialización para otro generador. Consulte la documentación de esas plataformas cuando use random_device en código con varias plataformas.
Por definición, los resultados de random_device no son reproducibles. Un efecto secundario de esto es que puede que se ejecute de forma significativamente más lenta que otros URNG. La mayoría de las aplicaciones que no tienen que ser seguras criptográficamente usan mt19937 o un motor similar, si bien probablemente quiera propagarlo con una llamada a random_device, como se muestra en el código de ejemplo.
[ir a la parte superior de la página]