线性同余法详解

一、基础概念

线性同余法(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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-12 13:17
下一篇 2024-12-12 13:17

相关推荐

  • Python实现一元线性回归模型

    本文将从多个方面详细阐述Python实现一元线性回归模型的代码。如果你对线性回归模型有一些了解,对Python语言也有所掌握,那么本文将对你有所帮助。在开始介绍具体代码前,让我们先…

    编程 2025-04-29
  • Python线性插值法:用数学建模实现精确预测

    本文将会详细介绍Python线性插值法的实现方式和应用场景。 一、插值法概述 插值法是基于已知数据点得出缺失数据点的一种方法。它常用于科学计算中的函数逼近,是一种基础的数学建模技术…

    编程 2025-04-27
  • Linux sync详解

    一、sync概述 sync是Linux中一个非常重要的命令,它可以将文件系统缓存中的内容,强制写入磁盘中。在执行sync之前,所有的文件系统更新将不会立即写入磁盘,而是先缓存在内存…

    编程 2025-04-25
  • 神经网络代码详解

    神经网络作为一种人工智能技术,被广泛应用于语音识别、图像识别、自然语言处理等领域。而神经网络的模型编写,离不开代码。本文将从多个方面详细阐述神经网络模型编写的代码技术。 一、神经网…

    编程 2025-04-25
  • nginx与apache应用开发详解

    一、概述 nginx和apache都是常见的web服务器。nginx是一个高性能的反向代理web服务器,将负载均衡和缓存集成在了一起,可以动静分离。apache是一个可扩展的web…

    编程 2025-04-25
  • Linux修改文件名命令详解

    在Linux系统中,修改文件名是一个很常见的操作。Linux提供了多种方式来修改文件名,这篇文章将介绍Linux修改文件名的详细操作。 一、mv命令 mv命令是Linux下的常用命…

    编程 2025-04-25
  • 详解eclipse设置

    一、安装与基础设置 1、下载eclipse并进行安装。 2、打开eclipse,选择对应的工作空间路径。 File -> Switch Workspace -> [选择…

    编程 2025-04-25
  • git config user.name的详解

    一、为什么要使用git config user.name? git是一个非常流行的分布式版本控制系统,很多程序员都会用到它。在使用git commit提交代码时,需要记录commi…

    编程 2025-04-25
  • Python安装OS库详解

    一、OS简介 OS库是Python标准库的一部分,它提供了跨平台的操作系统功能,使得Python可以进行文件操作、进程管理、环境变量读取等系统级操作。 OS库中包含了大量的文件和目…

    编程 2025-04-25
  • MPU6050工作原理详解

    一、什么是MPU6050 MPU6050是一种六轴惯性传感器,能够同时测量加速度和角速度。它由三个传感器组成:一个三轴加速度计和一个三轴陀螺仪。这个组合提供了非常精细的姿态解算,其…

    编程 2025-04-25

发表回复

登录后才能评论