C++随机数实例

转自:  http://blog.csdn.net/songshimvp1/article/details/47016805      

      旧式的随机数发生器沿用C的Rand()函数,这个函数会产生区间在[0,RAND_MAX]的伪随机数,且随机数近似可以看做符合均匀分布。
传统做法的弊端大致有两个:
1、经常需要做人肉变换随机数产生区间,且人肉变换过程中极易破坏原有的分布性质;
2、难以产生符合制定分布的随机数;

rand()函数的一些问题:

很多程序需要不同范围的随机数;

一些应用需要随机浮点数;

一些程序需要非均匀分布的数;

而程序员为了解决这些问题而试图转换rand生成的随机数的范围、类型或分布时,常常会引入非随机性。


一、C++ 11新标准针对这两点做了很大的改进——随机数发生器(定义在头文件random)。
新设施利用两部分来生成随机数:
(1)、random number engines(引擎类):负责生成原始随机数
(2)、random number distributions(分布类): 迫使生成的随机数在统计上满足指定的概率分布
STL预先指定了一系列的生成引擎,并且提供一个default_random_engine。default_random_engine会使用某个预定义的引擎,且不同编译器、不同平台的实现可能不同。

PS:有关详细的预定义引擎及分布类列表:


例子1:

  1. std::default_random_engine e;  
  2.   
  3. for (size_t i = 0; i < 10; i++) {  
  4.     cout << e() << endl;  
  5. }  
std::default_random_engine e;

for (size_t i = 0; i < 10; i++) {
    cout << e() << endl;
}
默认情况下,引擎产生的随机数范围在default_randm_engine::min和default_random_engine::max之间。


例子2:

可以指定随机数要符合的分布性质,例如说,在[5,20]上的均匀分布。

  1. std::default_random_engine e;  
  2. std::uniform_int_distribution<> u(5,20);  
  3. for (size_t i = 0; i < 10; i++) {  
  4.     cout << u(e) << endl;   //注意!  
  5. }  
std::default_random_engine e;
std::uniform_int_distribution<> u(5,20);
for (size_t i = 0; i < 10; i++) {
    cout << u(e) << endl;   //注意!
}

注意:我们传递给分布对象的是引擎对象本身,即u(e)。我们传递的是引擎本身,而不是它生成的下一个值,原因是某些分布可能需要调用引擎多次才能产生一个值。如果我们将调用写成u(e()),含义就变为将e生成的下一个值传递给u,这将会导致一个编译错误。


  1. //int rand_num = rand()%11;//rand获取0-10随机数的方法  
  2. //default_random_engine也同样可以指定随机数范围,那就是它的好基友分布类型:uniform_int_distribution  
  3. uniform_int_distribution<unsigned> u(0,10);//生成0到10(包含)均匀分布的随机数  
  4. default_random_engine e;//生成随机无符号数  
  5. for(int i=0;i<10;++i)  
  6. {  
  7.     cout << u(e) << endl;//u的范围范围便是e生成的随机数范围  
  8. }  
//int rand_num = rand()%11;//rand获取0-10随机数的方法
//default_random_engine也同样可以指定随机数范围,那就是它的好基友分布类型:uniform_int_distribution
uniform_int_distribution<unsigned> u(0,10);//生成0到10(包含)均匀分布的随机数
default_random_engine e;//生成随机无符号数
for(int i=0;i<10;++i)
{
	cout << u(e) << endl;//u的范围范围便是e生成的随机数范围
}

例子3:

关于种子。

  1. std::default_random_engine e(time(nullptr));  
  2. std::uniform_int_distribution<> u(5,20);  
  3. }  
std::default_random_engine e(time(nullptr));
std::uniform_int_distribution<> u(5,20);
}

       不过,由于time的精度是秒,所以在某些极端情况下,这也并不是一个非常好的选择。另外,如果程序作为一个自动过程的一部分反复运行,将time的返回值作为种子的方式就无效了,它可能多次使用的相同的种子。
       幸运的,C++11提供了一个非常友好的类:random_device。这个类的作用是,可以产生non-deterministic random numbers.
       这个类有可能产生真正的随机数,不过真是效果和具体实现有关。某些平台可能才用伪随机数作为底层实现也说不定呢…
       显然的,我们可以利用random_device去初始化我们的随机数种子

  1. std::random_device rd;  
  2. std::default_random_engine e(rd());  
  3. std::uniform_int_distribution<> u(5,20);  
  4. for (size_t i = 0; i < 10; i++) {  
  5.     cout << u(e) << endl;  
  6. }  
std::random_device rd;
std::default_random_engine e(rd());
std::uniform_int_distribution<> u(5,20);
for (size_t i = 0; i < 10; i++) {
    cout << u(e) << endl;
}

例子4:

为引擎设置种子的方式有两种,一种是创建时初始化种子,另一种是调用seed来初始化。

  1. default_random_engine e1;       //使用默认的种子  
  2. default_random_engine e2(2300);         //使用给定的种子  
  3.   
  4. default_random_engine e3;  
  5. e3.seed(2300);              //e3使用的种子与e2是一样一样的。  
default_random_engine e1;		//使用默认的种子
default_random_engine e2(2300);	        //使用给定的种子

default_random_engine e3;
e3.seed(2300);				//e3使用的种子与e2是一样一样的。

三、生成随机浮点数。

程序需要0到1之间的随机数。

最常用但是不正确的做法:从rand获得一个随机浮点数的方法是用rand()的结果除以RADN_MAX——即系统定义的rand可以生成的最大随机数的上界。——这种方法不正确的原因是:随机整数的精度通常低于随机浮点数,这样,有一些浮点数就永远不会生成了!

使用c++11的随机数发生器,可以让标准库来处理从随机整数到随机浮点数的映射。

  1. void main()  
  2. {  
  3.     default_random_engine e;  //生成无符号的随机整数  
  4.     uniform_real_distribution<double> u(0, 1);   //0到1(包含)的均匀分布  
  5.     for (size_t i = 0; i < 10; ++i)  
  6.     {  
  7.         cout << u(e) << " ";    //<span style="font-size: 18px;">从随机整数到随机浮点数的映射</span>  
  8.     }  
  9. }  
void main()
{
	default_random_engine e;  //生成无符号的随机整数
	uniform_real_distribution<double> u(0, 1);   //0到1(包含)的均匀分布
	for (size_t i = 0; i < 10; ++i)
	{
		cout << u(e) << " ";    //<span style="font-size: 18px;">从随机整数到随机浮点数的映射</span>
	}
}

四、生成非均匀分布的随机数

     比如伯努利分布、正态分布、抽样分布、泊松分布等。例子:生成一个正态分布的值序列,并画出值的分布。

  1. int main()  
  2. {  
  3.     default_random_engine e;        // generates random integers  
  4.     normal_distribution<> n(4,1.5); // 4为中心,标准差为1.5  
  5.     vector<unsigned> vals(9);       // nine elements each 0  
  6.   
  7.     for (size_t i = 0; i != 200; ++i)   
  8.         {  
  9.         unsigned v = lround(n(e));  // normal_distribution<>生成的是浮点值,使用头文件ctime中的lround函数将每个随机数舍入到最接近的整数  
  10.         if (v < vals.size())        // if this result is in range   
  11.             ++vals[v];          // count how often each number appears  
  12.     }  
  13.   
  14.     int sum = accumulate(vals.begin(), vals.end(), 0);  
  15.   
  16.     if (sum != 200)  
  17.         cout << "discarded " << 200 - sum << " results" << endl;  
  18.   
  19.     for (size_t j = 0; j != vals.size(); ++j)  
  20.         cout << j << ": " << string(vals[j], '*') << endl;//打印一个由*组成的string  
  21.   
  22.     return 0;  
  23. }  
int main()
{
	default_random_engine e;        // generates random integers
	normal_distribution<> n(4,1.5); // 4为中心,标准差为1.5
	vector<unsigned> vals(9);       // nine elements each 0

	for (size_t i = 0; i != 200; ++i) 
        {
		unsigned v = lround(n(e));  // normal_distribution<>生成的是浮点值,使用头文件ctime中的lround函数将每个随机数舍入到最接近的整数
		if (v < vals.size())        // if this result is in range 
			++vals[v];          // count how often each number appears
	}

	int sum = accumulate(vals.begin(), vals.end(), 0);

	if (sum != 200)
		cout << "discarded " << 200 - sum << " results" << endl;

	for (size_t j = 0; j != vals.size(); ++j)
		cout << j << ": " << string(vals[j], '*') << endl;//打印一个由*组成的string

	return 0;
}


特别注意:一个给定的随机数发生器一直会产生相同的随机数序列。一个函数如果定义了局部的随机数发生器,应该将其(引擎对象+分布对象)定义为static的。否则,每次调用函数都会生成相同的序列。

  1. #include<iostream>  
  2. #include<vector>  
  3. #include<random>  
  4. using namespace std;  
  5.   
  6. /*vector<unsigned> bad_rand() 
  7. { 
  8.     default_random_engine e;//随机数引擎类型之一,在大多数情况下(通常情况下)获得好的性能 
  9.     uniform_int_distribution<unsigned> u(0, 9); //随机数分布类型之一 
  10.     vector<unsigned> vec; 
  11.     for (size_t i = 0; i < 10; i++) 
  12.     { 
  13.         vec.push_back(u(e)); 
  14.     } 
  15.     return vec; 
  16. } 
  17.  
  18. void main() 
  19. { 
  20.     vector<unsigned> v1(bad_rand()); 
  21.     for (size_t k = 0; k < 10; k++) 
  22.     { 
  23.         cout << "v1:" << v1[k] << endl; 
  24.     } 
  25.     vector<unsigned> v2(bad_rand()); 
  26.     for (size_t k = 0; k < 10; k++) 
  27.     { 
  28.         cout << "v2:" << v2[k] << endl; 
  29.     } 
  30.     cout << ((v1 == v2) ? "equal" : "not equal" )<< endl;//打印“equal”! 
  31. //v1 : 2     
  32. //v1 : 2 
  33. //v1 : 4 
  34. //v1 : 5 
  35. //v1 : 4 
  36. //v1 : 1 
  37. //v1 : 9 
  38. //v1 : 5 
  39. //v1 : 8 
  40. //v1 : 3 
  41. //v2 : 2 
  42. //v2 : 2 
  43. //v2 : 4 
  44. //v2 : 5 
  45. //v2 : 4 
  46. //v2 : 1 
  47. //v2 : 9 
  48. //v2 : 5 
  49. //v2 : 8 
  50. //v2 : 3 
  51. //equal 
  52.     //随机数发生器此特性会使新手迷惑:即使生成的书看起来是随机的,但对一个给定的发生器,每次运行程序它都会返回相同的数值序列。 
  53.     //<strong>序列不变这一事实</strong>在调试时非常有用。但是另外一方面,使用随机数发生器的程序也必须注意这一点。 
  54. }*/  
#include<iostream>
#include<vector>
#include<random>
using namespace std;

/*vector<unsigned> bad_rand()
{
	default_random_engine e;//随机数引擎类型之一,在大多数情况下(通常情况下)获得好的性能
	uniform_int_distribution<unsigned> u(0, 9); //随机数分布类型之一
	vector<unsigned> vec;
	for (size_t i = 0; i < 10; i++)
	{
		vec.push_back(u(e));
	}
	return vec;
}

void main()
{
	vector<unsigned> v1(bad_rand());
	for (size_t k = 0; k < 10; k++)
	{
		cout << "v1:" << v1[k] << endl;
	}
	vector<unsigned> v2(bad_rand());
	for (size_t k = 0; k < 10; k++)
	{
		cout << "v2:" << v2[k] << endl;
	}
	cout << ((v1 == v2) ? "equal" : "not equal" )<< endl;//打印“equal”!
//v1 : 2    
//v1 : 2
//v1 : 4
//v1 : 5
//v1 : 4
//v1 : 1
//v1 : 9
//v1 : 5
//v1 : 8
//v1 : 3
//v2 : 2
//v2 : 2
//v2 : 4
//v2 : 5
//v2 : 4
//v2 : 1
//v2 : 9
//v2 : 5
//v2 : 8
//v2 : 3
//equal
	//随机数发生器此特性会使新手迷惑:即使生成的书看起来是随机的,但对一个给定的发生器,每次运行程序它都会返回相同的数值序列。
	//<strong>序列不变这一事实</strong>在调试时非常有用。但是另外一方面,使用随机数发生器的程序也必须注意这一点。
}*/

  1. //<span style="color:#ff0000;">解决上述问题的办法:</span>  
  2. vector<unsigned> good_rand()  
  3. {  
  4.     //<strong>解决这一问题的办法是</strong>:将引擎和关联的分布对象定义为“static”的。  
  5.     //将它们定义为“static”,因此它们在函数调用之间会保持住状态,从而每次调用都产生新的数据。  
  6.     static default_random_engine e;  
  7.     static uniform_int_distribution<unsigned> u(0, 9);  
  8.     vector<unsigned> vec;  
  9.     for (size_t i = 0; i < 10; i++)  
  10.     {  
  11.         vec.push_back(u(e));  
  12.     }  
  13.     return vec;  
  14. }  
  15.   
  16. void main()  
  17. {  
  18.     vector<unsigned> v1(good_rand());  
  19.     for (size_t k = 0; k < 10; k++)  
  20.     {  
  21.         cout << "v1:" << v1[k] << endl;  
  22.     }  
  23.     vector<unsigned> v2(good_rand());  
  24.     for (size_t k = 0; k < 10; k++)  
  25.     {  
  26.         cout << "v2:" << v2[k] << endl;  
  27.     }  
  28.     cout << ((v1 == v2) ? "equal" : "not equal") << endl;//打印“not equal”!  
  29. //v1 : 2  
  30. //v1 : 2  
  31. //v1 : 4  
  32. //v1 : 5  
  33. //v1 : 4  
  34. //v1 : 1  
  35. //v1 : 9  
  36. //v1 : 5  
  37. //v1 : 8  
  38. //v1 : 3  
  39. //v2 : 5  
  40. //v2 : 5  
  41. //v2 : 0  
  42. //v2 : 6  
  43. //v2 : 9  
  44. //v2 : 0  
  45. //v2 : 9  
  46. //v2 : 4  
  47. //v2 : 7  
  48. //v2 : 6  
  49. //not equal  
  50. }  
//<span style="color:#ff0000;">解决上述问题的办法:</span>
vector<unsigned> good_rand()
{
	//<strong>解决这一问题的办法是</strong>:将引擎和关联的分布对象定义为“static”的。
	//将它们定义为“static”,因此它们在函数调用之间会保持住状态,从而每次调用都产生新的数据。
	static default_random_engine e;
	static uniform_int_distribution<unsigned> u(0, 9);
	vector<unsigned> vec;
	for (size_t i = 0; i < 10; i++)
	{
		vec.push_back(u(e));
	}
	return vec;
}

void main()
{
	vector<unsigned> v1(good_rand());
	for (size_t k = 0; k < 10; k++)
	{
		cout << "v1:" << v1[k] << endl;
	}
	vector<unsigned> v2(good_rand());
	for (size_t k = 0; k < 10; k++)
	{
		cout << "v2:" << v2[k] << endl;
	}
	cout << ((v1 == v2) ? "equal" : "not equal") << endl;//打印“not equal”!
//v1 : 2
//v1 : 2
//v1 : 4
//v1 : 5
//v1 : 4
//v1 : 1
//v1 : 9
//v1 : 5
//v1 : 8
//v1 : 3
//v2 : 5
//v2 : 5
//v2 : 0
//v2 : 6
//v2 : 9
//v2 : 0
//v2 : 9
//v2 : 4
//v2 : 7
//v2 : 6
//not equal
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值