近两天在做一个传感器网络通信的模拟器,需要产生某些确定的范围的随机数模拟随机发出信号的传感器,小结一下:
使用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,不用在意)
好的,随机数生成就小结如上,新年快乐!