線性同餘法詳解

一、基礎概念

線性同餘法(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/zh-hk/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

發表回復

登錄後才能評論