随机数分为真随机数和伪随机数, 通常计算机的编程语言生成的是伪随机数,要借助操作系统的时间来撒“种子“。伪随机数的分布服从均匀分布,但是有内在的规律可循。

生成[M, N]的随机正整数( N − M ≤ 32767 N-M\le 32767 NM32767):

# include <iostream>
# include <cstdio>
# include <cstdlib>
# include <ctime>

const int M = 3;
const int N = 16;

int main() {
   
	int res = rand() % (N - M + 1) + M;
	std::cout << res;
	
	return 0;
}

此处的res的范围是[3, 16]
但此方法编译运行后每次运行都是得到相同的结果。如果需要每次运行的结果不同需要使用srand来撒种子。

# include <iostream>
# include <cstdio>
# include <cstdlib>
# include <ctime>

const int M = 3;
const int N = 16;

int main() {
   
	srand(unsigned(time(nullptr)));
	
	for (int i = 1; i <= 100; i++) {
   
		int res = rand() % (N - M + 1) + M;
		std::cout << res << std::endl;
	}

	return 0;
}

此处的res在[M, N]中服从均匀分布。

不同的编译器中RAND_MAX的值不同,本人的编译器中RAND_MAX的值为 32767 ( 2 15 − 1 ) 32767(2^{15}-1) 32767(2151),rand()函数的范围是[0, 32767], 需要 N − M ≤ 32767 N-M\le 32767 NM32767

	std::cout << RAND_MAX << std::endl;

生成[M, N]的随机正实数:

先通过double(rand()) / RAND_MAX得到[0, 1]的实数,再乘以范围得到[M, N]的实数。输出的平均值和 M + N 2 \frac{M+N}{2} 2M+N接近。

# include <iostream>
# include <cstdio>
# include <cstdlib>
# include <ctime>

const double M = 7.233;
const double N = 10.666;

int main() {
   
	srand(unsigned(time(nullptr)));

	long double sum = 0;
	for (int i = 1; i <= 1e6; i++) {
   
		double res = double(rand()) / RAND_MAX * (N - M) + M;
		
		sum += res;
	}

	std::cout << sum / 1e6;
	
	return 0;
}

生成[M~N]的随机正整数(非均匀分布):

如果只是OJ造数据之类对于随机数的离散程度没有较高的要求的话可以直接将两个rand()函数相乘来扩大随机数的范围。

# include <iostream>
# include <cstdio>
# include <cstdlib>
# include <ctime>

const int M = 3;
const int N = 1e9;

int main() {
   
	srand(unsigned(time(nullptr)));
	int max = 0;
	int min = 0x3f3f3f3f;
	for (int i = 1; i <= 1e8; i++) {
   
		int res = 1ll * rand() * rand() % (N - M + 1) + M;
		if (res > max) {
   
			max = res;
		}
		if (res < min) {
   
			min = res;
		}
// std::cout << res << std::endl;
	}
	std::cout << max << " " << min;
	return 0;
}

rand()函数的范围是[0, 32767], 两个rand()函数相乘的范围是[0, 1073676289],这样就扩大了所生成的随机数的范围。但此方法所得到的res不服从[M, N]的均匀分布。

生成[M~N]的随机正整数(均匀分布):

C++ 11中新增了库函数# include <random>, 生成随机数变得较为方便

# include <iostream>
# include <random>

typedef long long ll;

const ll M = 1e12;
const ll N = 3e12;

int main() {
   
	std::random_device rd;

	std::mt19937_64 mt(rd());

	std::uniform_int_distribution<long long> distribution(M, N);	
	
	long double sum = 0;
	for (int i = 1; i <= 1e6; i++) {
   
		ll res = distribution(mt);
		sum += res;
	}

	std::cout << ll(sum / 1e6);
	
	return 0;
}

输出的结果接近于 M + N 2 \frac{M+N}{2} 2M+N,服从均匀分布。
如果随机数只需要int范围,可以将std::mt19937_64替换成std::mt19937std::uniform_int_distribution<long long>替换成std::uniform_int_distribution<int>

生成[M~N]的随机正实数(均匀分布):

方法与上面的方法差不多,只需要关键字替换即可。

# include <iostream>
# include <random>

const double M = 7.233;
const double N = 10.666;

int main() {
   
	std::random_device rd;

	std::mt19937 mt(rd());

	std::uniform_real_distribution<double> distribution(M, N);	
	
	long double sum = 0;
	for (int i = 1; i <= 1e6; i++) {
   
		double res = distribution(mt);

		sum += res;
	}

	std::cout << sum / 1e6;
	
	return 0;
}

然而此方法(指C++11 中新增的生成随机数的方法)在Visual C++或者Linux的编译环境中编译后每次运行能够生成不同的随机数,但是在MinGW64的编译环境中编译后每次运行确不可以(至少本人在自己电脑的Dev-C++上测试时不可以)。在MinGW64的编译环境中生成服从均匀分布的整型或长整型数据本人还没有掌握。本来一开始想着用两个rand()函数拼成一个int型或许满足均匀分布,后来发现因为两个rand()函数不是独立分布的所以和两个rand()函数相乘的效果或许差不多。可能线性同余方式可以,但本人不会