一、基础概念
线性同余法(Linear congruential generator,简称LCG)是一种伪随机数生成器。其具体思想是以一种线性的方式对一个起始值进行递推计算,以得到一个序列化的伪随机数序列。LCG可以形式化地表示为:Xn+1 = (aXn + c) mod m,其中Xn+1是下一个伪随机数,Xn是当前伪随机数,a、c、m是先验设定好的参数,mod表示模运算。
LCG能够快速地生成大量的伪随机数,因此被广泛地应用于模拟、密码学等领域,同时也是许多编程语言中的随机数生成器的核心算法。
二、算法细节
在LCG中,参数a、c和m需要精心设计,否则可能导致随机数生成效果不佳。以下是一些需要注意的细节:
1.参数m
参数m应该为一个较大的质数,以确保随机数循环周期的长度较长,保证生成的伪随机数足够分散。同时,m也需要足够小,否则会影响算法执行的效率。
const int m = 2147483647; // 2^31 - 1
2.参数a
参数a需要满足以下条件:
1)a和m互质,以保证伪随机数的分散性。
2)a-1能够被m的所有质因子整除,以避免伪随机数循环周期过短。
const int a = 16807; // 随意选取的常数
3.参数c
参数c一般取0或1,不会对伪随机数的质量产生影响。如果使用C++标准库中的rand函数,其使用的是a=1103515245, c=12345, m=2147483647的LCG。
const int c = 0; // 常数c一般取0或1
三、优化方法
LCG是一种简单但不一定高效的伪随机数生成器,其生成的伪随机数分布不够均匀,容易被攻击者破解。为了弥补LCG的不足,可以采取以下优化方法:
1.超前迭代法
超前迭代法是一种通过多次递归来产生更高质量随机数的方法,它可以显著提高LCG的效率和随机性。在超前迭代法中,可以使用两个LCG,其中一个作为“控制器”,控制另一个的参数。这种方法可以产生出高质量的随机数序列,但需要占用相当大的空间。
// 线性同余法优化:超前迭代法
class Random {
public:
Random() {
// 控制器LCG
c_r = c0_r;
a_r = a0_r;
m_r = m0_r;
x_r = time(nullptr);
for (int i = 0; i < 10; i++) {
next_r();
}
}
int next_r() {
int x = x_r;
// LCG1作为控制器
x_r = (a0_r * x_r + c0_r) % m0_r;
// LCG2作为普通LGC
return (a_r * x + c_r) % m_r;
}
private:
const int c0_r = 1;
const int a0_r = 16807;
const int m0_r = 2147483647;
int c_r, a_r, m_r, x_r;
};
2.多项式同余法
多项式同余法是一种比LCG更高效的伪随机数生成算法。它使用多项式函数来代替线性函数,进一步提升了随机数生成效率和质量。不过,这种方法需要较大的空间,在实际应用时需要权衡空间和时间的开销。
// 多项式同余法
class Random {
public:
Random() {
for (int i = 0; i < degree_r; i++) {
a_r[i] = rand();
}
}
int next_r() {
int x = a_r[0];
for (int i = 1; i < degree_r; i++) {
x = (a_r[i] + x * seed_r) % m_r;
}
for (int i = 0; i < degree_r-1; i++) {
a_r[i] = a_r[i+1];
}
a_r[degree_r-1] = x;
return x;
}
private:
const int m_r = 2147483647;
const int seed_r = 212885;
const int degree_r = 10;
int a_r[degree_r];
};
四、应用实例
以下是一个应用LCG生成模拟数据的实例。在这个例子中,使用LCG和超前迭代法的组合产生了10000个随机数,用于绘制一个符合正态分布的数据图表。
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>
#include <string>
#include <ctime>
class Random {
public:
Random() {
c_r = c0_r;
a_r = a0_r;
m_r = m0_r;
x_r = time(nullptr);
for (int i = 0; i < 10; i++) {
next_r();
}
}
int next_r() {
int x = x_r;
x_r = (a0_r * x_r + c0_r) % m0_r;
return (a_r * x + c_r) % m_r;
}
private:
const int c0_r = 1;
const int a0_r = 16807;
const int m0_r = 2147483647;
int c_r, a_r, m_r, x_r;
};
int main() {
Random random;
std::vector<double> nums;
for (int i = 0; i < 10000; i++) {
nums.push_back(random.next_r() / static_cast<double>(random.m0_r));
}
// 使用Box-Muller变换将均匀分布转换为正态分布
std::vector<double> nums2;
for (int i = 0; i < 10000; i += 2) {
double u1 = nums[i];
double u2 = nums[i + 1];
double z1 = sqrt(-2 * log(u1)) * cos(2 * M_PI * u2);
double z2 = sqrt(-2 * log(u1)) * sin(2 * M_PI * u2);
nums2.push_back(z1);
nums2.push_back(z2);
}
// 统计数据
double sum = 0, avg = 0, stdev = 0;
for (auto num : nums2) {
sum += num;
}
avg = sum / nums2.size();
for (auto num : nums2) {
stdev += pow(num - avg, 2);
}
stdev = sqrt(stdev / (nums2.size() - 1));
// 输出结果到文件
std::ofstream out("output.txt");
for (auto num : nums2) {
out << num << std::endl;
}
out.close();
std::cout << "avg = " << avg << std::endl;
std::cout << "stdev = " << stdev << std::endl;
return 0;
}
原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/246774.html