[C++]产生随机数[通用方法]

近两天在做一个传感器网络通信的模拟器,需要产生某些确定的范围的随机数模拟随机发出信号的传感器,小结一下:

使用srand(time(0))设置随机数种子,也可以写成srand(time(NULL)),需要头文件<time.h>和<stdlib.h>

(c++建议使用<cstdlib>和<ctime>)

使用rand()函数产生随机数

一段小demo说明使用方法:

#include <bits/stdc++.h>
using namespace std;
int main(){
	srand(time(0));
	for(int i = 0 ; i < 10 ; i++){
		cout << rand() << " ";
	}
	cout << endl << RAND_MAX << endl;
	return 0;
}

运行结果:

注意:这里的srand()应该放到for循环的外面,放到里面就输出10个相同的值,可以自己试一下

下面来讨论范围问题:

注意到第一行是生成的10个随机数,第二行是打印出来的RAND_MAX的值(这个与特定的解释器有关,在我的电脑上这个值是32767),这个值是rand函数最大能够生成的值,但是这个值究竟在不在范围中呢?即生成的区间究竟是[0,RAND_MAX](0,RAND_MAX),(0,RAND_MAX],[0, RAND_MAX)中的哪一个呢?

我们可以自己测试一下,再写一段demo:

#include <bits/stdc++.h>
using namespace std;
int main(){
	ofstream outfile("result.txt", ios :: out);
	if(!outfile){
		cerr << "Open error" << endl;
		exit(1);
	}
	srand(time(0));
	bool minimum_is_0 = false;
	bool maximum_is_RAND_MAX = false;
	while(!(minimum_is_0 && maximum_is_RAND_MAX)){
		int temp = rand();
		outfile << temp << " ";
		if(temp == 0){
			minimum_is_0 = true;
		}
		else if(temp == RAND_MAX){
			maximum_is_RAND_MAX = true;
		}
	}
	return 0;
}

运行结果:

只用了0.48s就找到了这样的两个数0和RAND_MAX,我们可以打开日志文件验证一下:

可以看到我拖到底下的时候,是在0终止的,所以必然还会有一个RAND_MAX终止的情况,我多运行几遍这个程序,就应该每次会有1/2的概率以RAND_MAX终止(本学期所学概率论),且出现连续不以RAND_MAX结尾的机会应该越来越小

运行第二次:

运行第三次:

好的,成功了,看到了所需要的32767(RAND_MAX)

这样,我们就验证了rand()函数产生的随机数范围应该是[0, RAND_MAX]而不是其他的3个猜测

下面我们就可以根据这个实验结果来推出4个公式

首先我们使用rand() / RAND_MAX 就将区间[0, RAND_MAX]转移到了[0,1],这是很显然的

如果我们将其长度扩展为b-a,那么很显然,就只需要将rand() / RAND_MAX * (b - a)即可

然后将其平移到以a开头,即再加上a,就可以得到第一个公式[a,b]

rand() / RAND_MAX * (b - a) + a

但是,这样得到的会很奇怪,你会发现结果是一个两点分布,只会生成a和b两种值,而且生成a的概率差不多是生成b的概率的RAND_MAX - 1 倍(再次用到本学期概率论)为什么呢,因为C++并没有python那么友好地将所有的除法默认为以浮点数形式输出而是分为整数/整数和浮点数/浮点数,(记得刚开始学c++de时候就做一道求pi的近似值的题目,HNU实验题,学弟学妹都懂的,嘿嘿,里面要用到1/k,但开始结果怎么就是不对,最后改成1.0/k就对了)

所以这个公式目前并不完美,正确的求[a,b]的随机数的公式还要体现浮点数/浮点数,否则除法的结果就只能得到0和1两个边界和值而得不到中间的值:

所以正确的写法如下:

1.[a,b]的整数

int (double(rand()) / RAND_MAX * (b - a) + a )

注意:写成int ( double(rand() / RAND_MAX) * (b - a) + a ) 当然也是不对的,对0和1两个整数取double依然是0.0和1.0

写一段小demo测试一下:

#include <bits/stdc++.h>
using namespace std;
int main(){
	ofstream outfile("result.txt", ios :: out);
	if(!outfile){
		cerr << "Open error" << endl;
		exit(1);
	}
	int a = 0, b = 100;
	bool minimum_is_a = false;
	bool maximum_is_b = false;
	srand(time(0));
	while(!(minimum_is_a && maximum_is_b)){
		int temp = int(double(rand()) / RAND_MAX * (b - a) + a );
		if(temp == a){
			minimum_is_a = true;
			outfile << temp << " ";
		}
		else if(temp == b){
			maximum_is_b = true;
			outfile << temp << " ";
		}
	}
	return 0;
}

运行结果:

打开文本输出文件验证一下:

注意这次的逻辑是只有出现了a和b才会将其输出到文本文件中记录下来,并不是所有的数都记录                                                      下面思考如何去掉某一边的边界?

例如上面的这个小demo生成的是[0, 100]的随机数,那么我们想生成(0, 100]的随机数(整数)该怎么做呢?

首先考虑简单粗暴的方法,遇到不是0的才返回对应的随机数的值,即

#include <bits/stdc++.h>
using namespace std;
int random_num_generator(int a, int b){
	int temp = 0;
	do{
		temp = int(double(rand()) / RAND_MAX * (b - a) + a);
	}while(temp == a);
	return temp;
}
int main(){
	ofstream outfile("result.txt", ios :: out);
	if(!outfile){
		cerr << "Open error" << endl;
		exit(1);
	}
	srand(time(0));
	int a = 0, b = 100;
	for(int i = 0 ; i < pow(10, 6) ; i++){
		int temp = random_num_generator(a, b);
		outfile << temp << " ";
		if(temp == a){
			cout << "find a , failed" << endl;
			return 0;
		}
	}
	cout << "can't find a through pow(10, 6) random number" << endl;
	outfile.close();
	return 0;
}

运行结果:

                             

打开输出文件验证一下:

      这种方法似乎也不错,按照这种逻辑想要去掉边界或者是挖掉中间的某些点,加一个do-while循环判断返回值就可以了

所以我不打算写了,下面的这三个都可以用这种方法解决,甚至于说挖点这种更变态的要求都能用这种方法生成

2.(a,b)

3.(a,b]

4.[a,b)

小结一下:

1.产生特定范围的随机数(包括挖点)

第一步: [a,b]公式 int(double(rand()) / RAND_MAX * (b - a) + a)

第二步: 不要什么值就写一个do-while循环将其过滤掉不返回即可

2.产生特定范围的随机数且不重复 

另外一个问题就是如何生成不重复的特定范围的随机数,这个用一种广为流传但是效率奇高的方法-----STL大法

使用algorithm.h库中的random_shuffle函数

#include <bits/stdc++.h>
using namespace std;
int main(){
	ofstream outfile("300.txt", ios :: out);
	vector <int> vec_ran1, vec_ran2;
	for(int i = 0 ; i < 500 ; i++){
		vec_ran1.push_back(i);
		vec_ran2.push_back(i);
	}
	random_shuffle(vec_ran1.begin(), vec_ran1.end());
	random_shuffle(vec_ran2.begin(), vec_ran2.end());
	for(int i = 0 ; i < 300 ; i++){
		outfile << 0.1 << " " << vec_ran1[i] << " " << vec_ran2[i] << endl;
	}
	outfile.close();
	return 0;
}

这个非常好明白, 因为STL都是这么用的,作用就是将一个标准容器中的顺序打乱,而且每次调用,打乱的还不一样,上面就是生成2个长度为0-499的序列,然后调用random_shuffle方法进行打乱,然后取得前面的300个元素输出到文件(前面的0.1是仿真的时候需要用的另外一个parameter,不用在意)

好的,随机数生成就小结如上,新年快乐!

猜你喜欢

转载自blog.csdn.net/chenhanxuan1999/article/details/86560378
今日推荐