[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,不用在意)

 

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C 是一种通用的、面向过程的编程语言,用于开发各种软件,包括操作系统、应用程序和游戏等。C 语言的语法简洁易懂,在学习曲线上很容易上手。同时,它还具有很强的表达能力,使用者可以轻松控制计算机的低层细节。C 语言是许多其他语言(包括 C++、Java、Python 等)的基础,因此学习 C 语言对于任何想要成为计算机程序员的人都非常重要。 ### 回答2: 语言编程实现一个简单的猜数字游戏。 猜数字游戏的规则如下:计算机随机生成一个1至100之间的整数,玩家需要输入自己猜测的数字,计算机会根据猜测数字给出相应提示,直到玩家猜中为止。 下面是使用C语言实现猜数字游戏的代码: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { // 生成随机数种子 srand(time(0)); // 生成1至100之间的随机整数 int target = rand() % 100 + 1; int guess = 0; int attempts = 0; printf("欢迎来到猜数字游戏!\n"); // 猜数字的主循环 while (1) { printf("请输入你的猜测数字:"); scanf("%d", &guess); // 猜测次数加一 attempts++; // 判断玩家猜测是否正确 if (guess == target) { printf("恭喜你,猜中了!总共猜测了%d次。\n", attempts); break; } else if (guess < target) { printf("猜小了,请继续猜测。\n"); } else { printf("猜大了,请继续猜测。\n"); } } return 0; } ``` 以上就是使用C语言实现猜数字游戏的代码。玩家可以运行这个程序,根据提示输入猜测的数字,直到猜中为止。程序会根据玩家的猜测给出相应的提示,告诉玩家猜测的数字是猜小了还是猜大了,直到玩家最终猜中目标数字,游戏结束。 ### 回答3: 为什么 c 是一门经典编程语言? C 语言是一门经典的编程语言,具有以下几个原因: 首先,C 语言具有高度的可移植性和通用性。C 语言的设计初衷是为了编写操作系统,因此它可以在多个操作系统上运行,并且可以编写各种类型的程序。C 语言可以在不同的硬件平台上编译和执行,这使得它成为一种非常通用的语言。 其次,C 语言具有高效的性能。C 语言是一种底层语言,可以直接访问计算机的硬件和内存,这使得程序可以更高效地执行。C 语言不需要额外的运行时环境,因此可以生成非常小的可执行文件,提高了程序运行的速度。 另外,C 语言具有丰富的功能和灵活性。C 语言提供了丰富的库函数和标准函数,可以用于各种编程任务。另外,C 语言的语法简洁而灵活,使得程序员可以自由地组织代码和控制程序的执行流程。 此外,C 语言具有广泛的应用领域。由于其高效和可移植性,C 语言已广泛应用于系统开发、嵌入式系统、游戏开发等领域。C 语言也是许多其他编程语言的基础,如 C++、Java、Python 等,因此学好 C 语言对于掌握其他编程语言也非常有帮助。 总之,C 语言之所以成为一门经典的编程语言,是因为它具有高度的可移植性和通用性,高效的性能,丰富的功能和灵活性,以及广泛的应用领域。无论是对初学者还是对有经验的程序员来说,学习和掌握 C 语言都是非常有价值的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值