random como - Generar números aleatorios utilizando la biblioteca aleatoria C++11





funcion repetir (5)


Stephan T. Lavavej (stl) de Microsoft hizo una charla en Going Native sobre cómo usar las nuevas funciones aleatorias de C ++ 11 y por qué no usar rand() . En él, incluyó una diapositiva que básicamente resuelve tu pregunta. Copié el código de la diapositiva a continuación.

Puedes ver su charla completa aquí: channel9.msdn.com/Events/GoingNative/2013/…

#include <random>
#include <iostream>

int main() {
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_real_distribution<double> dist(1.0, 10.0);

    for (int i=0; i<16; ++i)
        std::cout << dist(mt) << "\n";
}

Utilizamos random_device una vez para sembrar el generador de números aleatorios llamado mt . random_device() es más lento que mt19937 , pero no necesita ser sembrado porque solicita datos aleatorios de su sistema operativo (que se originará en varias ubicaciones, como RdRand por ejemplo).

Al uniform_real_distribution esta pregunta / respuesta , parece que uniform_real_distribution devuelve un número en el rango [a, b) , donde desea [a, b] . Para hacer eso, nuestra uniform_real_distibution debería verse así:

std::uniform_real_distribution<double> dist(1, std::nextafter(10, DBL_MAX));

Como sugiere el título, estoy tratando de encontrar una forma de generar números aleatorios usando la nueva biblioteca c ++ 11 <random> . Lo he intentado con este código:

std::default_random_engine generator;
std::uniform_real_distribution<double> uniform_distance(1, 10.001);

El problema con el código que tengo es que cada vez que lo compilo y lo ejecuto, siempre genera los mismos números. Entonces mi pregunta es ¿qué otras funciones en la biblioteca al azar pueden lograr esto siendo verdaderamente al azar?

Para mi caso de uso particular, estaba tratando de obtener un rango dentro de [1, 10]




Tienes dos situaciones comunes. La primera es que quiere números aleatorios y no se preocupa demasiado por la calidad o la velocidad de ejecución. En ese caso, use la siguiente macro

#define uniform() (rand()/(RAND_MAX + 1.0))

eso le da p en el rango de 0 a 1 - epsilon (a menos que RAND_MAX sea mayor que la precisión de un doble, pero preocúpese por eso cuando llegue).

int x = (int) (uniform () * N);

Ahora da un entero aleatorio de 0 a N -1.

Si necesita otras distribuciones, debe transformar p. O a veces es más fácil llamar al uniforme () varias veces.

Si desea un comportamiento repetible, siembre con una constante, de lo contrario siembre con una llamada a la hora ().

Ahora, si le molesta la calidad o el rendimiento del tiempo de ejecución, vuelva a escribir el uniforme (). Pero de lo contrario, no toques el código. Mantenga siempre uniforme () en 0 a 1 menos épsilon. Ahora puede envolver la biblioteca de números aleatorios de C ++ para crear un uniforme mejor (), pero esa es una especie de opción de nivel medio. Si le molestan las características del RNG, también vale la pena invertir un poco de tiempo para comprender cómo funcionan los métodos subyacentes, y luego proporcionar uno. Así que tienes el control completo del código, y puedes garantizar que con la misma similitud, la secuencia siempre será exactamente la misma, independientemente de la plataforma o de la versión de C ++ a la que estés enlazando.




Aquí hay algo que acabo de escribir a lo largo de esas líneas ::

#include <random>
#include <chrono>
#include <thread>

using namespace std;

//==============================================================
// RANDOM BACKOFF TIME
//==============================================================
class backoff_time_t {
  public:
    random_device                      rd;
    mt19937                            mt;
    uniform_real_distribution<double>  dist;

    backoff_time_t() : rd{}, mt{rd()}, dist{0.5, 1.5} {}

    double rand() {
      return dist(mt);
    }
};

thread_local backoff_time_t backoff_time;


int main(int argc, char** argv) {
   double x1 = backoff_time.rand();
   double x2 = backoff_time.rand();
   double x3 = backoff_time.rand();
   double x4 = backoff_time.rand();
   return 0;
}

~




Mi biblioteca 'aleatoria' proporciona un envoltorio muy conveniente para las clases aleatorias de C ++ 11. Puedes hacer casi todas las cosas con un simple método 'get'.

Ejemplos:

  1. Número aleatorio en un rango

    auto val = Random::get(-10, 10); // Integer
    auto val = Random::get(10.f, -10.f); // Float point
    
  2. Random booleano

    auto val = Random::get<bool>( ) // 50% to generate true
    auto val = Random::get<bool>( 0.7 ) // 70% to generate true
    
  3. Valor aleatorio de un std :: initilizer_list

    auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
    
  4. Iterador aleatorio del rango del iterador o de todos los contenedores

    auto it = Random::get( vec.begin(), vec.end() ); // it = random iterator
    auto it = Random::get( vec ); // return random iterator
    

¡Y aún más cosas! Mira la página de github:

https://github.com/effolkronium/random




Primero, tienes que aprender a pensar como un abogado de lenguaje.

La especificación de C ++ no hace referencia a ningún compilador, sistema operativo o CPU en particular. Hace referencia a una máquina abstracta que es una generalización de sistemas reales. En el mundo de Language Lawyer, el trabajo del programador es escribir código para la máquina abstracta; el trabajo del compilador es actualizar ese código en una máquina concreta. Al codificar de forma rígida según la especificación, puede estar seguro de que su código se compilará y ejecutará sin modificaciones en cualquier sistema con un compilador compatible con C ++, ya sea hoy o dentro de 50 años.

La máquina abstracta en la especificación C ++ 98 / C ++ 03 es fundamentalmente de un solo hilo. Por lo tanto, no es posible escribir código C ++ multihebra que sea "completamente portátil" con respecto a la especificación. La especificación ni siquiera dice nada acerca de la atomicidad de las cargas y almacenes de memoria o el orden en el que pueden ocurrir las cargas y los almacenes, sin importar las cosas como las exclusiones mutuas.

Por supuesto, puede escribir código de múltiples subprocesos en la práctica para sistemas concretos particulares, como pthreads o Windows. Pero no hay una forma estándar de escribir código de subprocesos múltiples para C ++ 98 / C ++ 03.

La máquina abstracta en C ++ 11 es multiproceso por diseño. También tiene un modelo de memoria bien definido; es decir, dice lo que el compilador puede y no puede hacer cuando se trata de acceder a la memoria.

Considere el siguiente ejemplo, en el que dos subprocesos acceden simultáneamente a un par de variables globales:

           Global
           int x, y;

Thread 1            Thread 2
x = 17;             cout << y << " ";
y = 37;             cout << x << endl;

¿Qué podría hacer Thread 2?

Bajo C ++ 98 / C ++ 03, esto no es un comportamiento indefinido; La pregunta en sí no tiene sentido porque la norma no contempla nada llamado "hilo".

Bajo C ++ 11, el resultado es un comportamiento indefinido, porque las cargas y los almacenes no necesitan ser atómicos en general. Lo que puede no parecer una gran mejora ... Y, por sí mismo, no lo es.

Pero con C ++ 11, puedes escribir esto:

           Global
           atomic<int> x, y;

Thread 1                 Thread 2
x.store(17);             cout << y.load() << " ";
y.store(37);             cout << x.load() << endl;

Ahora las cosas se ponen mucho más interesantes. En primer lugar, aquí se define el comportamiento. El subproceso 2 ahora puede imprimir 0 0 (si se ejecuta antes del subproceso 1), 37 17 (si se ejecuta después del subproceso 1) o 0 17 (si se ejecuta después de que el subproceso 1 asigne a x pero antes de que se asigne a y).

Lo que no puede imprimir es 37 0 , porque el modo predeterminado para las cargas / tiendas atómicas en C ++ 11 es imponer la coherencia secuencial . Esto solo significa que todas las cargas y almacenes deben ser "como si" ocurrieran en el orden en que los escribiste dentro de cada hilo, mientras que las operaciones entre los hilos pueden intercalarse, sin embargo, el sistema lo desea. Por lo tanto, el comportamiento predeterminado de Atomics proporciona atomicidad y ordenamiento para cargas y almacenes.

Ahora, en una CPU moderna, garantizar la coherencia secuencial puede ser costoso. En particular, es probable que el compilador emita barreras de memoria en toda regla entre cada acceso aquí. Pero si su algoritmo puede tolerar cargas y almacenes fuera de orden; es decir, si requiere atomicidad pero no ordenamiento; es decir, si puede tolerar 37 0 como salida de este programa, entonces puede escribir esto:

           Global
           atomic<int> x, y;

Thread 1                            Thread 2
x.store(17,memory_order_relaxed);   cout << y.load(memory_order_relaxed) << " ";
y.store(37,memory_order_relaxed);   cout << x.load(memory_order_relaxed) << endl;

Cuanto más moderna sea la CPU, más probable será que sea más rápido que el ejemplo anterior.

Finalmente, si solo necesita mantener determinadas cargas y almacenes en orden, puede escribir:

           Global
           atomic<int> x, y;

Thread 1                            Thread 2
x.store(17,memory_order_release);   cout << y.load(memory_order_acquire) << " ";
y.store(37,memory_order_release);   cout << x.load(memory_order_acquire) << endl;

Esto nos lleva de vuelta a las cargas y almacenes ordenados, por lo que 37 0 ya no es una salida posible, pero lo hace con una sobrecarga mínima. (En este ejemplo trivial, el resultado es el mismo que la consistencia secuencial en toda regla; en un programa más grande, no lo sería).

Por supuesto, si las únicas salidas que desea ver son 0 0 o 37 17 , simplemente puede envolver un mutex alrededor del código original. Pero si has leído hasta aquí, apuesto a que ya sabes cómo funciona eso, y esta respuesta ya es más larga de lo que pretendía :-).

Por lo tanto, la línea de fondo. Los Mutexes son geniales, y C ++ 11 los estandariza. Pero a veces, por razones de rendimiento, desea primitivas de nivel inferior (por ejemplo, el patrón de bloqueo de doble control clásico). El nuevo estándar proporciona gadgets de alto nivel como exclusión mutua y variables de condición, y también proporciona gadgets de bajo nivel como los tipos atómicos y los diversos sabores de la barrera de la memoria. Así que ahora puede escribir rutinas concurrentes sofisticadas y de alto rendimiento completamente dentro del lenguaje especificado por el estándar, y puede estar seguro de que su código se compilará y se ejecutará sin cambios en los sistemas de hoy y de mañana.

Aunque, para ser sincero, a menos que sea un experto y esté trabajando en un código serio de bajo nivel, probablemente debería atenerse a las exclusiones y variables de condición. Eso es lo que pretendo hacer.

Para obtener más información sobre este tema, consulte esta publicación de blog .





c++ c++11 random range