C/C++ 随机数

在 C/C++中,随机数生成是一项常见的任务,通常用于模拟、游戏、密码学和统计等应用中。生成时一般会指定满足均匀分布、或者正态分布的随机数。下面,介绍下如何在 C 和 C++ 中生成满足均匀分布和正态分布的随机数。实现这一点,一般要有三个步骤:

  1. 设置随机数种子
  2. 根据种子值生成随机数
  3. 将随机数映射到某个分布内

1. C++ 随机数

C++ 中使用 random_device 作为随机数种子生成器,是一个硬件随机数生成设备,通常用于生成高质量的随机数种子。

在 C++ 中随机数生成器有 Mersenne Twister 生成器,也有线性同余生成器(Linear Congruential Generator,简称LCG),使用使用随机性更好的前者作为生成器。Mersenne Twister 生成器有两个版本 32 位和 64 位,后者生成的随机数范围更广泛,一般情况下,32 位生成器已经能够满足大部分场景下的随机数需求。

在 C++ 中提供两个类uniform_int_distribution、uniform_real_distribution 用于将生成的随机数映射到均匀分布,前者用于映射到均匀分布的整数,后者用于映射到均匀分布的浮点数。两个类都需要设置映射的数值范围。normal_distribution 用于将随机数映射到满足均值和标准差的某个正态分布中。

#if 1
#include <random>
#include <iostream>
using namespace std;


void test()
{
	// 1. 生成随机数种子
	random_device rd;
	int seed = rd();
	cout << "seed = " << seed << endl;

	// 2. 根据种子生成随机数
	// Mersenne Twister 32 版本,提供更大的周期和更广的范围。
	mt19937 generator1(rd());
	int number1 = generator1();
	cout << "number1 = " << number1 << endl;

	// Mersenne Twister 64位版本,提供更大的周期和更广的范围。
	mt19937_64 generator2(rd());
	int number2 = generator2();
	cout << "number2 = " << number2 << endl;
	 
	// 默认随机数生成器,默认使用的是 mt19937
	std::default_random_engine generator3(rd());
	int number3 = generator3();
	cout << "number3 = " << number3 << endl;

	// 3. 映射到分布
	// 整数均匀分布:将生成器生成的数字映射到整数均匀分布
	uniform_int_distribution<int> distribution1(100000, 200000);
	int number4 = distribution1(generator1);
	cout << "number4 = " << number4 << endl;

	// 小数均匀分布:将生成器生成的数字映射到小数均匀分布
	uniform_real_distribution<double> distribution2(0.0, 1.0);
	double number5 = distribution2(generator1);
	cout << "number2 = " << number2 << endl;

	
	// 正态分布:将生成器生成的数字映射到正态分布
	normal_distribution<double> distribution3(0.0, 1.0);
	double number6 = distribution3(generator1);
	cout << "number6 = " << number6 << endl;
}


int main()
{
	test();
	return 0;
}
#endif
seed = 1306554885
number1 = -2089015614
number2 = 1517304759
number3 = -1872929550
number4 = 168310
number2 = 1517304759
number6 = -0.425118

2. C 随机数

C 使用 rand 函数根据随机数种子计算出 0-32767 之间的随机数,并且该随机数满足均匀分布。

#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <memory.h>


void test01()
{
	// 1. 获得时间戳作为随机数种子
	time_t seed = time(NULL);
	printf("seed = %lld\n", seed);
	srand((unsigned int)seed);

	// 2. 产生随机数, 函数能够返回的最大随机数32767
	int number = rand();
	printf("number = %d\n", number);


	// 3. 计算指定范围
	int lower = 0;
	int upper = RAND_MAX;

	// 4. 查看随机数分布:默认是均匀分布
	int *record = (int *)malloc(sizeof(int) * (RAND_MAX + 100));
	memset(record, 0, sizeof(int) * (RAND_MAX + 100));
	for (int i =0; i < 100000000; ++i)
	{
		int random_number = rand() % (upper - lower + 1) + lower;
		record[random_number] += 1;
	}

	for (int i = RAND_MAX - 10; i < RAND_MAX + 5; ++i)
	{
		printf("%d %d\n", i, record[i]);
	}
}
seed = 1698314607
32757 3012
32758 3073
32759 2966
32760 3004
32761 3041
32762 2993
32763 3018
32764 3080
32765 3069
32766 3001
32767 3040
32768 0
32769 0
32770 0
32771 0

如果产生超出 0-32767 范围的随机数,rand 函数是力不从心的。究其原因就是随机产生的数字范围太小,我们可以在 rand 函数的基础上,构造一个较大的随机数,再将其映射到一个较小的区间内来实现。

#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <memory.h>
#include <math.h>

long long random_int_number(long long lower, long long upper)
{	

	// 将负数便宜到正数区间
	long long offset = 0;
	if (lower < 0)
	{
		offset = 0 - lower;
		lower = lower + offset;
		upper = upper + offset;
	}

	// 构造一个随机较大的整数
	long double number = 0.0;
	for (int i = 0; i < 8; ++i)
	{
		number += ((rand() % 10) * powl((long double)10, (long double)i));
	}

	// 将较大随机数映射到指定的区间 [lower, upper] 区间
	long long result = (long long)number % (upper - lower + 1) + lower;

	// 再将随机数映射到用户指定的区间
	result -= offset;

	return  result;
}

void test02()
{
	srand((unsigned int)time(NULL));
	

	int arr[21] = { 0 };
	for (int i = 0; i < 1000000; ++i)
	{
		long long number = random_int_number(-10, 10);
		arr[number + 10] += 1;
	}

	for (int i = 0; i < 20; ++i)
	{
		printf("%d %d\n", i-10, arr[i]);
	}
}


int main()
{
	test02();
	return 0;
}
-10 47405
-9 47574
-8 47770
-7 47740
-6 47976
-5 47464
-4 47650
-3 47539
-2 47582
-1 47435
0 47696
1 47647
2 47575
3 47931
4 47458
5 47300
6 47667
7 47983
8 47470
9 47271

如果要生成满足正态分布的随机数,可以使用 Box-Muller 变换,示例代码如下:

double normal_random(double mean, double stddev) 
{
	double u1 = (double)rand() / RAND_MAX;
	double u2 = (double)rand() / RAND_MAX;

	// Box-Muller 变换是一种用于生成满足标准正态分布(均值为0,标准差为1)的随机数的统计方法
	double z = sqrt(-2.0 * log(u1)) * cos(2.0 * 3.1415926 * u2);

	return mean + stddev * z;
}
未经允许不得转载:一亩三分地 » C/C++ 随机数
评论 (0)

2 + 5 =