隨機數(一)
CoreCLR 中的 Random 類
在 .NET Framework 中,偽隨機數生成器是使用 System.Random 類來實現的。Microsoft 已經開源了 .NET Core Runtime (CoreCLR),我們可以從 GitHub 下載:
$ git clone https://github.com/dotnet/coreclr.git
$ cd coreclr
$ view src/mscorlib/src/System/Random.cs
如果不想下載 CoreCLR 的話,這個 Random 類的原始碼也可以在 ideone 中檢視。這個 Random.cs 源程式,Microsoft 寫得有點隨意了,有些不好的地方,如第 28 行:
private const int MZ = 0;
這個 MZ 以後根本就沒有用到,可以刪除。第 78 行:
Seed = 1;
也可以刪除。第 99、100 行和第 112、113 行:
int locINext = inext;
int locINextp = inextp;
inext = locINext;
inextp = locINextp;
這四行也可以刪除,第 102 至 110 行中出現的 locINext 和 locINextp 用 inext 和 inextp 代替。
第 55、56 和 98 等行的變數宣告也可以刪除,改為在後面的變數賦值時進行宣告,以符合變數的最小作用域原則。第 140 行:
bool negative = (InternalSample()%2 == 0) ? true : false;
應改為:
bool negative = InternalSample() % 2 == 0;
或者乾脆刪除 negative 這個變數,併入第 141 行的 if 語句。
上圖是在 Windows 7 作業系統中使用 ILSpy 檢視 mscorlib 中的 System.Random 類的結果。可以看出,經過 C# 編譯器編譯後,區域性變數名沒有保留下來,所以 ILSpy 就使用 num、num2、num3 等變數名。很明顯,最後一句 Seed = 1; 完全沒有必要,可以刪除。在上圖中,第 55 和 56 行的變數宣告已經刪除。
從上圖可以看出,第 140 行已經按照我前面講的進行了優化。
MsRandom 類
下面的 MsRandom 類就是把 Random 類稍做修改而得到的:
namespace Skyiv {
using System;
public class MsRandom
{
const int M = int.MaxValue;
int[] seeds = new int[56];
int n1 = 0, n2 = 21;
public MsRandom() : this(Environment.TickCount) { }
public MsRandom(int seed)
{
int j = 161803398-((seed==int.MinValue)?int.MaxValue:Math.Abs(seed));
seeds[55] = j;
for (int z, k = 1, i = 1; i < 55; i++, j = seeds[z]) {
seeds[z = (21 * i) % 55] = k;
if ((k = j - k) < 0) k += M;
}
for (int k = 1; k < 5; k++)
for (int i = 1; i < 56; i++) {
seeds[i] -= seeds[1 + (i + 30) % 55];
if (seeds[i] < 0) seeds[i] += M;
}
}
protected virtual double Sample() {return (InternalSample()*(1.0/M));}
public virtual double NextDouble() { return Sample(); }
public virtual int Next() { return InternalSample(); }
public virtual int Next(int max)
{
if (max < 0) throw new ArgumentOutOfRangeException(nameof(max));
return (int)(Sample() * max);
}
public virtual int Next(int min, int max)
{
if (min > max) throw new ArgumentOutOfRangeException(nameof(min));
long z = (long)max - min;
if (z <= int.MaxValue) return (int)(Sample()*z) + min;
return (int)((long)(GetSampleForLargeRange()*z) + min);
}
public virtual void NextBytes(byte [] buffer)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
for (int i = 0; i < buffer.Length; i++)
buffer[i] = (byte)(InternalSample() % (byte.MaxValue + 1));
}
int InternalSample()
{
if (++n1 >= 56) n1 = 1;
if (++n2 >= 56) n2 = 1;
int z = seeds[n1] - seeds[n2];
if (z == M) z--;
if (z < 0) z += M;
return seeds[n1] = z;
}
double GetSampleForLargeRange()
{
double d = InternalSample();
if (InternalSample() % 2 == 0) d = -d;
return (d + (int.MaxValue - 1)) / (2 * (uint)int.MaxValue- 1);
}
}
}
測試程式
我們使用下面的 C# 程式來測試一下:
using System;
namespace Skyiv.Test
{
static class RandomTester
{
static readonly Random rand = new Random();
static bool Test(int a, int b)
{
Console.Write('.');
for (var i = 0; i < 123; i++) {
var seed = rand.Next();
var r1 = new Random(seed);
var r2 = new MsRandom(seed);
for (var j = 0; j < 987654; j++)
if (r1.Next(a, b) != r2.Next(a, b)) {
Console.WriteLine(" Fail.");
return false;
}
}
return true;
}
static void Main()
{
Console.WriteLine(Environment.OSVersion);
Console.WriteLine("CLR " + Environment.Version);
Console.Write("Test ");
if (!Test(int.MinValue, int.MaxValue)) return;
for (var i = 0; i < 9; i++) {
var a = rand.Next(int.MinValue, int.MaxValue);
if (!Test(a, rand.Next(a, int.MaxValue))) return;
}
Console.WriteLine(" OK.");
}
}
}
編譯和執行
在 Linux 作業系統中編譯和執行:
$ mcs RandomTester.cs MsRandom.cs
$ mono RandomTester.exe
Unix 4.6.3.1
CLR 4.0.30319.42000
Test .......... OK.
在 Windows 作業系統中執行:
Microsoft Windows [版本 6.1.7601]
版權所有 (c) 2009 Microsoft Corporation。保留所有權利。
C> RandomTester.exe
Microsoft Windows NT 6.1.7601 Service Pack 1
CLR 4.0.30319.42000
Test .......... OK.
來源
實際上,這個 Random 程式來自《C語言數值演算法程式大全(第二版)》第七章 隨機數:
7.1.2 可移植的隨機數生成程式
最後,我們把 Knuth 建議[4]的可移植程式提供給大家,我們已將它轉換成為現在的程式,稱作 ran3。這個 ran3 根本不是基於線性同餘的方法,而是以一個相減方法為基礎(見[5])。
Knuth, D.E. 1981. Seminumerical Algorithms, 2nded, vol. 2 of The Art of Computer Programming (Raeding, MA: Addison-Wesley), §§3.2~3.3. [4]
Kahaner, D., Moler, C, and Nash, S. 1989, Numerical Methods and Software (Englewood Cliffs, NJ: Prentice Hall), Chapter 10. [5]
這個 ran3 是 C 語言程式,Microsoft 把它改寫為 C# 語言程式。
此外,這本書還告誡我們不要使用系統提供的隨機數生成器:
7.1.1 系統提供的隨機數生成程式
多數 c 語言內部蘊含有可以初始化並能生成“隨機數”的 ANSI 庫函式。典型程式為:
......
在本章中,這一節最重要的內容是,對系統提供的 rand() 產生很大很大的懷疑,因為它同我們剛剛敘述的內容很類似。由於不合理的函式 rand() 使一些科學論文的結果值得懷疑,如果將這些科學論文從圖書館的書架上全部取走的話,那麼在每個書架上就會留出一拳之寬空隙。
參考資料
相關文章
- [隨機數詳解]生成一個隨機數,生成指定範圍的隨機數及隨機陣列去重隨機陣列
- 聊一聊隨機數安全隨機
- 隨機數隨機
- 【概率論】一維隨機變數隨機變數
- 數學一|概統|一、隨機事件和機率隨機事件
- python生成隨機數、隨機字串Python隨機字串
- 隨機數(三)隨機
- 隨機數(二)隨機
- 生成隨機數隨機
- groovy : 隨機數隨機
- 一文搞懂Java隨機數生成Java隨機
- 一個隨機數的類c++隨機C++
- MySQL取得某一範圍隨機數MySql隨機
- 隨機範圍小數和隨機範圍整數隨機
- [Z]Oracle 的隨機數、隨機日期和時間、隨機字串Oracle隨機字串
- JavaScript 生成隨機數JavaScript隨機
- C++ 隨機數C++隨機
- 明明的隨機數隨機
- 隨機數擴充隨機
- mysql生成隨機數MySql隨機
- 隨機數漫談隨機
- Js Math隨機數JS隨機
- Linux Shell 生成隨機數和隨機字串Linux隨機字串
- Python隨機數與隨機字串詳解Python隨機字串
- 數學一|概統|四、隨機變數的數字特徵隨機變數特徵
- 對於隨機數的一些分析隨機
- 一維隨機變數及其概率分佈隨機變數概率分佈
- Matlab 隨機生成兩個數值之間的隨機數Matlab隨機
- 數學一|概統|二、隨機變數及其分佈隨機變數
- matlab產生隨機數或隨機矩陣Matlab隨機矩陣
- 偽隨機數是什麼?偽隨機數生成方法有哪些?隨機
- 編寫一個程式,獲取10個1至20的隨機數,要求隨機數不能重複。隨機
- 從oracle表中隨機取記錄,產生隨機數和隨機字串Oracle隨機字串
- 高效產生一組不重複的隨機數隨機
- 隨機數生成器隨機
- java隨機數生成原理Java隨機
- Java 隨機數探祕Java隨機
- R產生隨機數隨機