深度分析:Microsoft .NET Framework System.Random 的 C++ 復刻實現

深度分析:Microsoft .NET Framework Random 的 C++ 復刻實現


核心原理與算法結構

本實現基于 Knuth 減隨機數生成器(Subtractive Random Number Generator),是 .NET Framework 中 System.Random 的精確復刻。其核心特點包括:

  1. 狀態驅動
    使用長度為 56 的整數數組 SeedArray 作為內部狀態,通過兩個指針 inextinextp 控制隨機數生成過程。
  2. 種子初始化
    通過復雜的數學變換將種子擴散到整個狀態數組,確保初始狀態高度隨機化。
  3. 抗線程安全
    每個 Random 實例維護獨立狀態,天然支持多線程(無需鎖)——只要每個線程使用自己的實例。
  4. 確定性輸出
    相同種子必然產生相同序列,適用于需要可重現隨機結果的場景(如仿真)。

可視化原理圖

  +---------------------+|     初始化階段       |<-----------------++---------------------+                  ||                                  || 1. 用種子計算初始狀態數組        || 2. 4輪混合操作強化隨機性         |v                                  |+---------------------+                  ||     生成階段         |                  |+---------------------+                  ||                                  || 1. 移動指針 inext/inextp        | 狀態反饋| 2. 取 SeedArray[inext]           || 3. 減 SeedArray[inextp]          || 4. 調整范圍并更新狀態            |v                                  |+---------------------+                  ||  輸出隨機數 (int)    |------------------++---------------------+ || 可選轉換v+---------------------+|  輸出隨機數 (double) |+---------------------+

關鍵組件結構圖

Random
-int SeedArray[56]
-int Seed
-int inext
-int inextp
+Random()
+Random(int seed)
+int& GetSeed()
+void SetSeed(int seed)
+static uint64_t GetTickCount()
+int Next()
+double NextDouble()
+int Next(int minValue, int maxValue)

深度算法分析

1. 初始化過程(狀態數組構建)
// 核心初始化代碼片段
int num = (Seed == INT_MIN) ? INT_MAX : abs(Seed);
int num2 = 161803398 - num;  // 魔法常數初始化
SeedArray[55] = num2;// 擴散種子到整個數組
int num3 = 1;
for (int i = 1; i < 55; i++) {int num4 = 21 * i % 55;  // 非線性分布SeedArray[num4] = num3;num3 = num2 - num3;      // 差分擴散if (num3 < 0) num3 += INT_MAX;num2 = SeedArray[num4];
}// 4輪混合強化隨機性
for (int j = 1; j < 5; j++) {for (int k = 1; k < 56; k++) {SeedArray[k] -= SeedArray[1 + (k + 30) % 55];if (SeedArray[k] < 0) SeedArray[k] += INT_MAX;}
}

數學原理
使用線性同余和模運算的組合,通過 161803398(黃金比例相關常數)確保種子微小變化導致狀態劇變。

2. 隨機數生成(抗線程安全關鍵)
int num = inext;  // 當前指針
int num2 = inextp; // 歷史指針// 環形緩沖區遍歷(56為周期)
if (++num >= 56) num = 1;
if (++num2 >= 56) num2 = 1;// 核心生成算法
int num3 = SeedArray[num] - SeedArray[num2];
if (num3 == INT_MAX) num3--;     // 避免溢出
if (num3 < 0) num3 += INT_MAX;   // 確保非負// 更新狀態
SeedArray[num] = num3;
inext = num;
inextp = num2;

線程安全性

  • 無全局變量
  • 所有狀態封裝在實例內
  • 無跨線程共享數據

精度與分布驗證

整數生成 (Next())
  • 范圍:[0, INT_MAX](均勻分布)
  • 周期:約 2^55(理論值)
浮點數生成 (NextDouble())
double Random::NextDouble() noexcept {int num = Next();if ((Next() % 2 == 0)) num = -num;  // 隨機符號double num2 = num + 2147483646.0;   // 映射到正區間return num2 / 4294967293.0;         // 歸一化到 [0,1)
}

數學驗證
輸出范圍 = [0, (2147483646*2)/4294967293] ≈ [0, 0.999...]

范圍生成 (Next(min, max))
// 處理大范圍場景(避免整數溢出)
long long num = (long long)maxValue - minValue;
if (num <= INT_MAX) {return (int)((Next() * 4.6566128752457969E-10) * num) + minValue;
}
return (int)((long long)(NextDouble() * num) + minValue);

常數解釋
4.6566128752457969E-10 = 1 / 2147483647(INT_MAX 的倒數)


完整源代碼

頭文件:ppp/Random.h
#pragma once#include <ppp/stdafx.h>namespace ppp {struct Random {private:int SeedArray[56];int Seed = 0;int inext = 0;int inextp = 0;public:Random() noexcept;Random(int seed) noexcept;int& GetSeed() noexcept;void SetSeed(int seed) noexcept;static uint64_t GetTickCount() noexcept;int Next() noexcept;double NextDouble() noexcept;int Next(int minValue, int maxValue) noexcept;};
}
實現文件:ppp/Random.cpp
#include <ppp/stdafx.h>
#include <ppp/Random.h>
#include <ppp/threading/Executors.h>namespace ppp {Random::Random() noexcept : Random(static_cast<int>(GetTickCount())) {}Random::Random(int seed) noexcept : Seed(seed), inext(0), inextp(0) {memset(SeedArray, 0, sizeof(SeedArray));}int& Random::GetSeed() noexcept { return Seed; }void Random::SetSeed(int seed) noexcept { Seed = seed; }uint64_t Random::GetTickCount() noexcept {return ppp::threading::Executors::GetTickCount();}int Random::Next() noexcept {// ====================== 初始化階段 ======================{int num = (Seed == INT_MIN) ? INT_MAX : abs(Seed);int num2 = 161803398 - num;SeedArray[55] = num2;int num3 = 1;for (int i = 1; i < 55; i++) {int num4 = 21 * i % 55;SeedArray[num4] = num3;num3 = num2 - num3;if (num3 < 0) {num3 += INT_MAX;}num2 = SeedArray[num4];}for (int j = 1; j < 5; j++) {for (int k = 1; k < 56; k++) {SeedArray[k] -= SeedArray[1 + (k + 30) % 55];if (SeedArray[k] < 0) {SeedArray[k] += INT_MAX;}}}inext = 0;inextp = 21;Seed = 1;}// ====================== 隨機數生成階段 ======================{int num = inext;int num2 = inextp;if (++num >= 56) {num = 1;}if (++num2 >= 56) {num2 = 1;}int num3 = SeedArray[num] - SeedArray[num2];if (num3 == INT_MAX) {num3--;}if (num3 < 0) {num3 += INT_MAX;}SeedArray[num] = num3;inext = num;inextp = num2;Seed = num3;}return Seed;}double Random::NextDouble() noexcept {int num = Next();if ((Next() % 2 == 0) ? true : false) {num = -num;}double num2 = num;num2 += 2147483646.0;return num2 / 4294967293.0;}int Random::Next(int minValue, int maxValue) noexcept {if (minValue == maxValue) {return minValue;}if (minValue > maxValue) {maxValue = minValue;}long long num = (long long)maxValue - (long long)minValue;if (num <= INT_MAX) {return (int)(((double)Next() * 4.6566128752457969E-10) * (double)num) + minValue;}return (int)((long long)(NextDouble() * (double)num) + minValue);}
}
  1. 核心生成算法

    int num3 = SeedArray[num] - SeedArray[num2];
    if (num3 == INT_MAX) num3--;
    if (num3 < 0) num3 += INT_MAX;
    
  2. 浮點數生成優化

    double num2 = num;
    num2 += 2147483646.0;           // 轉換到正數范圍
    return num2 / 4294967293.0;      // 歸一化到[0,1)
    
  3. 大范圍整數處理

    long long num = (long long)maxValue - minValue;
    if (num <= INT_MAX) {// 使用整數優化路徑
    } else {// 使用浮點數路徑處理大范圍
    }
    

線程安全證明

此實現通過以下設計實現線程安全:

  1. 無靜態數據:所有狀態存儲在實例成員中
  2. 無共享狀態:每個實例維護獨立的狀態數組
  3. 無鎖操作:所有操作都是原子性的整數運算
  4. 無跨線程訪問:狀態變量不暴露給外部

性能與適用場景

  1. 性能
    • 單次調用約 50 條指令(不含初始化)
    • 比 Mersenne Twister 更快,適合高頻調用
  2. 適用場景
    • 游戲邏輯(非加密)
    • 科學模擬
    • 隨機算法(如 QuickSort 隨機樞軸)
  3. 不適用
    • 密碼學(非加密安全)
    • 真隨機需求(需結合硬件熵源)

關鍵優勢:在保持 .NET 兼容性的同時,通過純頭文件實現和零動態分配,完美適配 C++ 高性能場景。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/89830.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/89830.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/89830.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[論文閱讀] 人工智能 | 在非CUDA硬件上運行幾何學習:基于Intel Gaudi-v2 HPU的PyTorch框架移植實踐

在非CUDA硬件上運行幾何學習&#xff1a;基于Intel Gaudi-v2 HPU的PyTorch框架移植實踐 論文標題&#xff1a;PyTorch-based Geometric Learning with Non-CUDA Processing Units: Experiences from Intel Gaudi-v2 HPUs arXiv:2507.01031 (cross-list from cs.LG) PyTorch-ba…

Python-多線程-threading

1 需求 2 接口 3 示例 4 參考資料 Python treading 模塊 | 菜鳥教程

2025年- H91-Lc199-- 62.不同路徑(多維動態規劃)--Java版

1.題目描述 2.思路 dp含義&#xff1a;代表到當前位置的路徑數 遞推公式&#xff1a;dp[i][j]dp[i-1][j]dp[i][j-1] dp數組初始化&#xff0c;我們要確保第一行和第一列是有值的. dp數組的遍歷順序&#xff1a;我們需要從左往右遍歷&#xff0c;從上往下遍歷。并且把第一行和第…

char 不是 Java 中的 2 字節(16 位)嗎? 為什么用 UTF-8 編碼寫入時,一個中文要占 3 個字節?

char 不是 Java 中的 2 字節&#xff08;16 位&#xff09;嗎&#xff1f; 為什么用 UTF-8 編碼寫入時&#xff0c;一個中文要占 3 個字節&#xff1f; ? 一、Java 中的 char 是什么&#xff1f; Java 的 char 是一個 固定大小的 2 字節&#xff08;16 位&#xff09;類型&am…

【Elasticsearch】檢索排序 分頁

檢索排序 & 分頁 1.測試數據準備2.排序功能2.1 簡單字段排序2.2 多字段排序2.3 日期排序 3.分頁功能3.1 基礎分頁3.2 深度分頁&#xff08;不推薦大數據量使用&#xff09;3.3 使用 search_after 進行高效分頁 4.綜合示例&#xff1a;高亮排序分頁5.實踐建議 1.測試數據準備…

Delta、Jackknife、Bootstrap

用班級平均身高的案例&#xff0c;展示 ?Delta、Jackknife、Bootstrap? 的完整計算過程。 ?0. 數據準備? ?原始數據&#xff08;4個學生的身高&#xff09;??&#xff1a; 真實均值&#xff08;目標統計量&#xff09;??&#xff1a; ?1. Delta 方法&#xff08;公式…

企業智腦技術架構設計:緊貼企業場景規劃面向未來的發展趨勢與實現路徑

摘要 本文深入探討了企業智腦技術架構的設計理念與發展趨勢&#xff0c;分析了當前企業智能化轉型的技術需求與挑戰&#xff0c;提出了一個面向未來的企業智腦技術架構設計方案。文章從底層技術支撐、核心能力構建、應用場景適配、安全合規保障以及未來發展路徑五個維度展開論…

新手向:Python方向講解

從NASA火星任務到TikTok推薦算法&#xff0c;從自動化腳本到量子計算&#xff0c;Python用import antigravity重新定義了編程邊界 一、設計哲學&#xff1a;優雅明確的編程禪學 Python之禪&#xff08;import this&#xff09;&#xff1a; 優美勝于丑陋&#xff08;Beautifu…

Chrome谷歌瀏覽器插件ModHeader,修改請求頭,開發神器

文章目錄一、介紹與下載二、使用一、介紹與下載 ModHeader顧名思義就是讓我們可以自定義HTTP請求頭或者是重寫響應頭&#xff0c;包括新增請求頭/響應頭或者覆蓋Chrome瀏覽器設置的請求頭的默認值&#xff0c;同時還可以根據URL Pattern來只對特定網站生效。 有條件的同學可以…

SEW:無監督預訓練在語音識別中的性能-效率權衡

摘要 本文研究了自動語音識別&#xff08;ASR&#xff09;中預訓練模型的性能-效率權衡問題。我們聚焦于 wav2vec 2.0&#xff0c;并形式化了多種影響模型性能和效率的架構設計。基于所有觀察結果&#xff0c;我們提出了 SEW&#xff08;Squeezed and Efficient Wav2vec&#…

linux系統部署express+vue項目

一、準備階段&#xff1a; 1、安裝linux上所需要的環境&#xff1a;npm nodejs nginx pm2 //安裝 npm&#xff08;Node 包管理器&#xff09; sudo apt install npm//判斷是否安裝成功 npm -v//安裝 Node.js&#xff08;可以根據需要選擇版本&#xff09; sudo apt inst…

PixiJS教程(004):點擊事件交互

1.6 事件交互實現要求&#xff1a;點擊寶劍&#xff0c;修改寶劍的顏色。1??實現代碼&#xff1a; // 為精靈添加交互事件 sprite.interactive true; sprite.on(click, () > {// 點擊精靈時&#xff0c;改變精靈的顏色sprite.tint Math.random() * 0xFFFFFF; });說明&am…

創客匠人助力家庭教育IP破局:從0到1打造創始人個人品牌全攻略

一、IP定位&#xff1a;細分賽道的精準錨定與用戶畫像構建 在家庭教育8000億市場規模的競爭中&#xff0c;創始人IP的差異化定位成為破局關鍵。創客匠人通過“標簽化定位”工具&#xff0c;幫助教育者鎖定垂直領域&#xff0c;如親子溝通、青春期教育等細分賽道。以景麗霞老師…

使用堅果云擴容Zotero同步空間的簡單快捷方法

本文介紹基于堅果云的WebDAV協議&#xff0c;用于文獻管理軟件Zotero的文件同步&#xff0c;從而實現Zotero存儲空間擴容的方法。 在之前的文章Zotero文獻管理軟件入門使用方法&#xff1a;軟件下載、文獻導入、引文插入&#xff08;https://blog.csdn.net/zhebushibiaoshifu/a…

Java啟動腳本

Java啟動腳本 編寫代碼&#xff0c;然后打包 Java-1.0-SNAPSHOT.jar public class test {public static void main(String[] args) {System.out.println("Hello IDEA");} }編寫運行腳本 #!/bin/sh WORKDIR$(cd $(dirname $0); pwd) cd $WORKDIRexport JAVA_OPTS"…

VSCode使用ssh遠程連接阿里云

1. 終端選擇 Windows使用PowerShell Ubuntu和Mac使用Terminal 2. 設置ssh 2.1. 第一臺電腦 生成密鑰 ssh-keygen -o -t rsa -b 4096 -C "emailexample.com" 按三次回車 查看密鑰 cat ~/.ssh/id_rsa.pub 拷貝密鑰&#xff0c;粘貼到服務器的密鑰框中 2.2. 第…

XLSR-Wav2Vec2:用于語音識別的無監督跨語言表示學習

摘要 本文提出了 XLSR&#xff0c;該方法通過從多種語言的原始語音波形中預訓練單個模型&#xff0c;以學習跨語言的語音表示。我們基于 wav2vec 2.0 構建模型&#xff0c;該方法通過對掩蔽后的潛在語音表示解決對比任務進行訓練&#xff0c;并聯合學習在多種語言之間共享的潛…

圖靈完備之路(數電學習三分鐘)----數據選擇器與總線

1.數據選擇器之前我們學習了邏輯與算數的計算&#xff0c;得知兩個數字之間的加減和與或的結果是不同的&#xff0c;而一個通用的數字電路不可能只有一個功能&#xff0c;所以我們將在本節引入電路選擇器這一“器件”&#xff0c;來實現對兩個輸入的運算方式的選擇&#xff0c;…

Linux下如何設置CUDA的路徑

今天遇到一個關于CUDA的問題&#xff0c;我要跑的深度學習代碼&#xff0c;他里面有cuda編程&#xff0c;需要編譯。但是你運行就報錯。 代碼提示我大段報錯。 (score-denoise) ubuntuGPUA10002:~/wbd/score-denoise_Transformerdepth20$ python train.py Detected CUDA fil…

js樹的排序

樹 樹的前中后序遍歷 樹是一種重要的非線性數據結構&#xff0c;尤其是二叉樹。二叉樹的遍歷是操作樹的基礎&#xff0c;主要有前序遍歷、中序遍歷和后序遍歷三種方式。 前序遍歷 訪問順序&#xff1a;根結點 -> 左子樹 -> 右子樹。 遍歷規則&#xff1a;首先訪問根結…