C ++: 58 --- random number random special facilities of the standard library (random number engine type, random distribution category)

A, C ++ Random Number Library Overview

C library functions rand Overview

  • Before the new standards emerge, C and C ++ are dependent on a simple C library function rand to generate random numbers
  • This function generates a pseudo-random integers uniformly distributed , random number for each range between a maximum and a system associated with 0 (see RAND_MAX constants: https://blog.csdn.net/qq_41453285/article/details/103456053 )
  • rand function have some questions:
    • Some applications require random float
    • Some programs require a non-uniform distribution of the book
    • If programmers to attempt to solve the above problems rand conversion range of the random number generated, or the type of distribution, often introduced nonrandom
  • The definition of a random number in the header file library of random addresses these issues through a set of cooperating classes:
    • Random number engine classes: a class engine may generate a random number sequence unsigned
    • Random distribution classes: a class distribution class generated using a specified type of engine, within a given range, subject to a certain probability distribution of the random number

Random number engine class

  • Library defines three classes implement different algorithms to generate a random number
  • The library also defines three adapters, you can modify a given sequence generated by the engine
  • Engine and engine adapter classes are templates. Different points with random numbers are not tired of parameters, the parameters of these engines is more complex and requires in-depth understanding of mathematical knowledge specific engine used
  • The library also defines several types constructed from the engine and adapter type . default_random_engine parameter type is a type of engine types alias parameter variables used in order to obtain a good performance under normal circumstances. The library also defines several classes, which are a special case of complete version of the engine or adapter. It is defined as follows:

Random distribution class

  • In addition to always generate bool type bernoulli_distribution, the other distribution classes are templates. Each template takes a single type parameter, which indicates the type of distribution of results generated

  • Distribution classes with other template we have used different, they limit what we can specify the type of template type:

    • Some distribution templates can only be used to generate floating-point numbers

    • While others can only be used to generate the certificate template

  • The following description:
    • By types as template_name <RealT> to indicate the distribution of floating point numbers. These templates, we can use the float, double, or long double substitution RealT
    • Similar, INTT represents integer type requires a built-in, but does not include any type bool or char type. We can use short, int, long, long long, unsigned short, unsigned int, unsigned long, unsigned long long instead IntT
  • Distribution template defines a default template type parameter . The default parameter distribution is an integer int, float generated template of the default argument is double
  • Each profile has this constructor specific parameters . Some parameter indicates the scope of distribution. The scope and range of different iterators. They are inclusive

Second, the basic engine class using the class distribution and

  • In the application, we generally do not directly use the output of the random number engine class , because these are the original random numbers may not match our needs. When we use the engine-based random number generates a random number, typically with a random distribution of the number of classes is generated within a specified range

Random number engine class

  • Random number function object class is an engine class, defines a call operator () , the operator no arguments and returns a random integer unsigned
  • We can call a random number engine class object to generate the original random number:
#include <iostream>
#include <random>
using namespace std;

int main()
{
    std::default_random_engine e;     //生成随机无符号数
    for (size_t i = 0; i < 10; ++i)   //使用调用运算符(),来生成一个随机数
        std::cout << e() << std::endl;
    return 0;
}

  • Library defines a number of random number engine class (see above), except that different performance and quality of randomness . Wherein each compiler will specify a type as default_random_engine. This type generally has the most commonly used features
  • The following figure shows the operation of the random number engine class:

Random distribution class

  • In order to obtain a number within the specified range, we first engine class using a random number generates a random number using a random number and then distributed to generate an object class of a distributed type
  • Distribution class is also a function object. Distribution class defines a call operator (), a random number engine accepts as an argument. Distributed object using its random number generating engine parameters, and maps it to the specified distribution
  • E.g:
    • Uniform_int_distribution unsigned values ​​generated using uniformly distributed, and provides the maximum value (9) and the minimum value (0) to give the desired
    • The engine is then passed to the distribution of class Class
#include <iostream>
#include <string>
#include <random>
using namespace std;

int main()
{
    std::uniform_int_distribution<unsigned> u(0, 9); //随机数分布类
    std::default_random_engine e;                    //随机数引擎类
    for (size_t i = 0; i < 10; ++i)
        std::cout << u(e) << " ";                    //分布类调用引擎类,来生成指定范围内的数
    std::cout << std::endl;
    return 0;
}

  • Note: We passed the object to the distribution of the object is the engine itself, rather than the value of the engine generated objects . E.g:
std::uniform_int_distribution<unsigned> u(0, 9); //分布类
std::default_random_engine e;                    //引擎类

u(e);   //错误的
u(e()); //正确的
  • The supported operations random distribution classes:

 

Third, the random number generation range of the number of engine classes

  • In the above we can see that the random number engine output object class default_random_engine rand function similar
  • The difference is:
    • Rand function generation number range is between 0 and RAND_MAX
    • And the range of the random number generated by the unsigned integer type engine in a system-defined. A range of engine types may be obtained by calling the object's member min and max
  • E.g:
std::default_random_engine e;
std::cout << "min: " << e.min() << '\n' << "max: " << e.max() << std::endl;

Fourth, the numerical sequence generated by the engine class (important)

  • There is often a characteristic of a random number makes the novice confused: even if generated random number appears, but for a given generator , each run of the program will return the same sequence of values
  • Sequence unchanged this fact is very useful when debugging. On the other hand, using a random number generator program must take into account this page

Case presentation

  • E.g. following a write function, to generate a Vector, containing 100 uniformly distributed between 0 to 9, a random number:
//这个函数虽然语法没有错误,但是每次运行都返回相同的随机数
std::vector<unsigned> bad_randVec()
{
    std::default_random_engine e;
    std::uniform_int_distribution<unsigned> u(0, 9);

    std::vector<unsigned> ret;
    for (size_t i = 0; i < 100; ++i)
        ret.push_back(u(e));

    return ret;
}
  • The above function each call will generate the same random number, even if re-run the program shut down as well. Thus, vector each time elements are returned in the same
  • We write a code to verify:
int main()
{
    std::vector<unsigned> v1(bad_randVec());
    std::vector<unsigned> v2(bad_randVec());
	
    std::cout << ((v1 == v2) ? "equal" : "not equal") << std::endl;
    return 0;
}

The distribution object definitions and associated engine is static

  • If you want to return every time a random number is not the same, it should be defined random number and a random number distribution engine class class static
  • Why are defined as static on it?
    • Because the variable is defined as static, for the first time after the definition will be permanently present in the program does not end with the execution of the function and destruction
    • When performing a random distribution of the first class, 100 generates a random number. Then perform the next time the same random number based distribution, 100 will receive the next random number, so ......
#include <iostream>
#include <vector>
#include <random>
using namespace std;

std::vector<unsigned> bad_randVec()
{
    static std::default_random_engine e;
    static std::uniform_int_distribution<unsigned> u(0, 9);

    std::vector<unsigned> ret;
    for (size_t i = 0; i < 100; ++i)
        ret.push_back(u(e));

    return ret;
}

int main()
{
    std::vector<unsigned> v1(bad_randVec());
    std::vector<unsigned> v2(bad_randVec());
	
    std::cout << ((v1 == v2) ? "equal" : "not equal") << std::endl;
    return 0;
}

Fifth, set a random number generator seed

  • Above we described, the same random number sequence for distribution and distribution type engine, each time the random number generator is the same
  • But we hope that each time you run the program will randomly generate different results , can provide a seed for this purpose. The seed is a value, the engine can use it again starts to generate a new location from the sequence of random numbers
  • There are two ways to set the seeds for the engine:
    • When you create an object to provide seed
    • Call the function to set the engine of seed

Case presentation

  • The following defines the four engines:
    • Different seed before two engines, thus generating a different sequence
    • The same seeds after two engines, thus generating the same sequence
int main()
{
    std::default_random_engine e1;
    std::default_random_engine e2(2177483646);
    std::default_random_engine e3;
    e3.seed(32767);
    std::default_random_engine e4(32767);

    for (size_t i = 0; i != 100; ++i) {
        if (e1() == e2()) //false
            std::cout << "unseeded match at iteration: " << i << std::endl;
        if (e3() != e4()) //false
            std::cout << "seeded differs at iteration: " << i << std::endl;
    }
    return 0;
}
  • Therefore, the results of the above program runs without any display

time function

  • Choose a good seed, and most other good things to generate random numbers involved in the same, it is extremely difficult. Probably the most common method is to call system function time
  • This function is defined in the header file ctime in, it returns to the current time from a specific number of seconds elapsed
  • time function takes a single argument pointer, for writing to the data structure of time. If the secondary pointer is empty, the function simply returns the time
  • E.g:
#include <iostream>
#include <vector>
#include <random>
#include <ctime>
using namespace std;

int main()
{
    std::default_random_engine e1(std::time(0));
    for (size_t i = 0; i < 5;++i)
        std::cout << e1() << std::endl;
    return 0;
}

  • Since the return time to time in seconds, so this method is applicable to the generation interval of second stage seed or more reference

Six random distribution class Revisited

  • Unsigned number of the random number generated by the engine, the probability of each number generated in the range is the same. And applications often require different types or random distribution. By defining standard library to meet both requirements of different random distribution class
  • Distribution class objects and engine class objects work together to produce the desired result

Generating a random real number (uniform_real_distribution class)

  • If you use the rand () function, then:
    • If rand () function generates a floating-point number, using the method rand () and RAND_MAX result is divided, i.e., the upper bound of the rand may generate a system-defined maximum number of random
    • This method is not correct because the random integer precision floating point is generally lower than random, so that there are some floating point values ​​are never generated
  • uniform_real_distribution distribution classes for handling of floating point numbers
  • Engine class using a random number to provide a random integer, floating point numbers and then passed to uniform_real_distribution
  • For example, the following code is used to generate a floating point number between 0 and 1 :
#include <iostream>
#include <random>
using namespace std;

int main()
{
    std::default_random_engine e; //生成无符号整数
    //生成0到1之间的浮点数(均匀分布)
    std::uniform_real_distribution<double> u(0, 1);

    //依次获取10个浮点数
    for (size_t i = 0; i < 10; ++i)
        std::cout << u(e) << " ";
    std::cout << std::endl;
    return 0;
}

Use the default result type distribution class

  • Classes are distributed template, the template having a single type of parameter, data indicating the type of distribution of the generated random numbers . (There is one exception to this will be bernoulli_distribution below)
  • Each branch has a default template template argument . E.g:
    • Generating a random distribution based double floating point value generated
    • Generating a random integer value int value generated class distribution
  • E.g:
//默认生成浮点数,因此我们可以省略模板实参类型
std::uniform_real_distribution<> u(0, 1);

Generating a random number in a non-uniform division

  • In addition to the number of correctly generated within the specified range, a further advantage of the new standard library may generate a random number is non-uniformly distributed . In fact, the library defines 20 kinds of distribution patterns (see article beginning)
  • Case presentation:
    • We generate sequence a normal distribution, and draw the distribution of values
    • Since normal_distribution floating point numbers, we use the program function lround each random number is rounded to the nearest integer
    • We will generate the number 200, as the center thereof to 4, the standard deviation of 1.5
    • Since the normal distribution is used, we expect the number of generated about 99% are between 0-8 (inclusive)
#include <iostream>
#include <string>
#include <vector>
#include <random>
#include <cmath> //lround
using namespace std;

int main()
{
    std::default_random_engine e;         //生成随机整数
    std::normal_distribution<> n(4, 1.5); //均值为4,标准差为1.5
    std::vector<unsigned> vals(9);        //9个元素均为0

    for (size_t i = 0; i != 200; ++i) 
    {
        unsigned v = std::lround(n(e));   //舍入到最接近的整数
        if (v < vals.size())              //如果结果在范围内
            ++vals[v];                    //统计每个数出现了多少次
    }

    //循环打印
    for (size_t j = 0; j != vals.size(); ++j)
        std::cout << j << ": " << std::string(vals[j], '*') << std::endl;
    return 0;
}
  • As result of the program, we can not clearly see the uniform distribution

bernoulli_distribution类

  • We said random number distribution above is a class template, but bernoulli_distribution is an exception, it is a class and not a template
  • This distribution is always returns a bool value , it returns the probability that a true constant, the default probability is 0.5
  • Case presentation:
    • Write a program that interact with users
    • One of the players - you or the program - must perform
    • We can use a range of values ​​is uniform_int_distribution 0-to-1 to select the first player, but can also be used to complete the selection Bernoulli distribution
    • Assume that the function has been named play of the game
    • Use a do while loop to repeatedly prompt the user for the game
#include <iostream>
#include <string>
#include <random>
using namespace std;

bool play(bool value)
{
    //..进行游戏	
}

int main()
{
    std::string resp; //输入y代表继续游戏,输入其他退出游戏
    std::default_random_engine e;  //e应该保持状态,因此必须定义在循环外面
    std::bernoulli_distribution b;

    do
    {
        bool first = b(e); //随机返回true或false
        //传递谁先进行游戏的指示
        std::cout << (first ? "We go first" : "You get to go first") << std::endl;
        std::cout << ((play(first)) ? "Sorry you lost" : "congrats,you won") << std::endl;
    } while (std::cin >> resp&&resp[0] == 'y');

    return 0;
}

  • The use bernoulli_distribution Another benefit is that we can first adjust overview . For example following a small advantage to the program:
std::bernoulli_distribution b(.55);
  • If you use the above definition, the program has a 55/45 chance to advance
Released 1504 original articles · won praise 1063 · Views 430,000 +

Guess you like

Origin blog.csdn.net/qq_41453285/article/details/104675186