产生随机数的局限以及处理方法

计算机产生随机数具有一定的局限性,首先是它的序列不是真正的随机序列,因为计算机是图灵机,相同的输入必然产生相同的输出,要产生不同的随机数必须让输入不一样,于是可以考虑把几乎每时每刻都在变化的系统时间也作为输入,这就是以时间作为种子产生随机数的方法。还有一个最要命的限制就是这样产生的随机数上限太小了,只有32767,。以下下验证其最大值不会超过32767的程序,为了对这个程序改进使得最大值能更加大,并且有可能取得这个更加大的范围里的每一个数,改进的程序跟没有该进的程序已经合并在一起,只要改动EXTENT的值,EXTENT=0表示直接产生的随机数,EXTENT=1表示处理后产生的随机数。

原始数据产生方法如下:

for(int i=0;i<MAXCOUNT;i++)//系统产生的随机数
		a[i]=b[i]=rand()%MAXIMUM;

改进主要代码如下:

for(int i=0;i<MAXCOUNT;i++)//在系统产生的随机数的基础上,处理得到的随机数
		a[i]=b[i]=rand()*rand()%MAXIMUM;
注意!!不能简单认为将每个元素平方就可以解决问题了,因为这样随机数的范围确实是变大了,只是有很多数字是随机数无法表示的,因为它们并不是某个数的平方数。

以下为源代码:

#include "stdafx.h"
#include<iostream>
#include<iomanip>
#include<time.h>
#define MAXIMUM   100000//MAXIMUM表示最大的随机数
#define MAXCOUNT  10000//MAXCOUNT表示随机数的个数
#define WIDTH     14  //输出格式控制的宽度
#define INTERVALCOUNT 100//统计的区间总数
#define EXTENT 1         //预编译命令1和0分别表示处理以及不处理
using namespace std;
/***************************************************************
函数原型:void RandomTest(int a[],int n);
函数参数:a[]表示测试的数组,n表示数组元素个数
函数功能:随机性检测,把它们分到100个区间里面去
****************************************************************/
void RandomTest(int a[],int n){
	int interval=MAXIMUM/INTERVALCOUNT;
	int Count[INTERVALCOUNT];
	for(int i=0;i<INTERVALCOUNT;i++)
		Count[i]=0;
	for(int i=0;i<n;i++)
		Count[a[i]/interval]++;//把这个数加入到对应的区间中去
	cout<<"统计结果如下:"<<endl<<"区间"<<setw(WIDTH)<<"个数"<<endl;
	for(int i=0;i<INTERVALCOUNT;i++){
		cout<<"["<<setw(5)<<i*interval<<","<<setw(6)<<(i+1)*interval<<")"<<setw(6)<<Count[i]<<endl;
	}
}
int _tmain(int argc, _TCHAR* argv[])
{	
	srand((unsigned)time(NULL));//随机数种子
	int a[MAXCOUNT],b[MAXCOUNT];
#if !EXTENT
	for(int i=0;i<MAXCOUNT;i++)//系统产生的随机数
		a[i]=b[i]=rand()%MAXIMUM;
	for(int i=1;i<MAXCOUNT;i++)
		if(a[0]<a[i])
			a[0]=a[i];
	cout<<"系统产生的随机数最大是:"<<a[0]<<endl;
#else
	for(int i=0;i<MAXCOUNT;i++)//在系统产生的随机数的基础上,处理得到的随机数
		a[i]=b[i]=rand()*rand()%MAXIMUM;
	for(int i=1;i<MAXCOUNT;i++)
		if(a[0]<a[i])
			a[0]=a[i];
	cout<<"处理后产生的随机数最大是:"<<a[0]<<endl;
#endif
	cout<<"随机数总共有"<<MAXCOUNT<<"个,其理论范围是:0~"<<MAXIMUM<<endl;//随机数个数以及范围
	for(int i=1;i<MAXCOUNT;i++)
		if(a[0]>a[i])
			a[0]=a[i];
	for(int i=1;i<MAXCOUNT;i++)
		if(b[0]<b[i])
			b[0]=b[i];
	cout<<"其实际范围为:"<<a[0]<<"~"<<b[0]<<endl;
	RandomTest(a,MAXCOUNT);
	return 0;
}

(1)系统直接产生随机数的情况,这时没能取得最大值32767,可以看到随机数的分布比较均匀



(2)系统直接产生随机数的情况,这时取得最大值32767,可以看到随机数的分布比较均匀



(3)对系统直接产生随机数进行处理的情况,这时能的取得最大值远大于32767,上限可以自己确定,只要比MAXIMUM小的话,可以看到随机数的分布并不均匀,这并不说明这种处理不理想,这是因为随机数的范围变大了很大,而随机数的个数并没有增加,每个区间的随机数少了,容易不均匀,就好像抛1000次硬币正面朝上的频率比抛10次正面朝上的频率更接近1/2.

最后说一点,从32767这个系统产生的最大随机数来看,容易看出一些东西,比如系统产生随机数的方法。

因为学计算机的都知道,32767是一个很特殊的数,它的二进制表示全部都是1,一共15个,所以计算机产生随机数的机理如下:

计算机有一个很大的二进制的随机数表以保证产生的数字足够随机。然后利用系统时间产生一个种子,得到第一个偏移量,从这个位置开始,依次读出其后15个位置的数值,组成一个二进制数,这个二进制数的上限正好是32767,为了做到与上一次产生的随机数没有关系,再从当前15位二进制数的末端开始(而不是原来位置的下一位或者下几位)再次读取连续的15个二进制代码,得到下一个随机数(如果需要的话)。由此我们可以知道,如果两个不同的时刻,产生的种子是一样的话,那么产生的随机序列就一定是相同的(最起码短的那个序列跟长的那个序列的前面一部分完全一样)。因此,这样随机序列也不是真正的随机序列,我们称之为伪随机序列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值