隨機數的定義為:產生的所有數字毫無關係.
在實際應用中很多地方會用到隨機數,比如需要生成唯一的訂單號.
在c#中獲取隨機數有三種方法:
一.random 類
random類預設的無參建構函式可以根據當前系統時鐘為種子,進行一系列演算法得出要求範圍內的偽隨機數.
這種隨機數可以達到一些要求較低的目標,但是如果在高併發的情況下,random類所取到的系統時鐘種子接近甚至完全一樣,就很有可能出現重複,這裡用迴圈來舉例
這個例子會輸出10個相同的"隨機數".
突顯出的問題:因為random進行偽隨機數的演算法是固定的,所以根據同乙個種子計算出的數字必然是一樣的.而以當代計算機的執行速度,該迴圈幾乎是在瞬間完成的,種子一致,所以會出現10次迴圈輸出同一隨機數的情況.
有的時候使用random生成隨機數的時候往往不是隨機的 這是為什麼呢?
隨機數生成方法可以說是任何程式語言必備的功能,它的重要性不言而言,在c#中我們通常使用random類生成隨機數,在一些場景下,我卻發現random生成的隨機數並不可靠,在下面的例子中我們通過迴圈隨機生成5個隨機數:
for (int i = 0; i < 5; i++)
這段**執行後的結果如下所示:
2140400647 2140400647 2140400647 2140400647 2140400647
通過以上結果可知,隨機數類生成了5個相同的數,這並非我們的預期,為什麼呢?為了弄清楚這個問題,零度剖析了微軟官方的開源random類,發現在c#中生成隨機數使用的演算法是線性同餘法,經百科而知,這種演算法生成的不是絕對隨機,而是一種偽隨機數,線性同餘法演算法的的公式是:
第n+1個數 = ( 第n個數 * a + b) % m
上面的公式中a、b和m分別為常數,是生成隨機數的因子,如果之前從未通過同乙個random物件生成過隨機數(也就是呼叫過next方法),那麼第n個隨機數為將被指定為乙個預設的常數,這個常數在建立乙個random類時被預設值指定,random也提供乙個建構函式允許開發者使用自己的隨機數因子,這一切可通過微軟官方開源**看到:
public random() : this(environment.tickcount) public random(int seed)
通過預設建構函式建立random類時,乙個environment.tickcount物件作為因子被預設傳遞給第二個建構函式,environment.tickcount表示作業系統啟動後經過的毫秒數,計算機的運算運算速度遠比毫秒要快得多,這導致乙個的具有毫秒精度的因子參與隨機數的生成過程,但在5次迴圈中,我們使用了同乙個毫秒級的因子,從而生成相同的隨機數,另外,第n+1個數的生成與第n個數有著直接的關係。
在上面的例子中,假設系統啟動以來的毫秒數為888毫秒,執行5次迴圈用時只有0.1毫秒,這導致在迴圈中建立的5個random物件都使用了相同的888因子,每次被建立的隨機物件又使用了相同的第n個數(預設為常數),通過這樣的假設我們不難看出,上面的結果是必然的。
現在我們改變這個格局,在迴圈之外建立乙個random物件,在每次迴圈中引用它,並通過它生成隨機數,並在同乙個物件上多次呼叫next方法,從而不斷變化第n個數,**如下所示:
random random = new random(); for (int i = 0; i < 5; i++)
執行後的結果如下所示:
391098894 1791722821 1488616582 1970032058 201874423
我們看到這個結果確實證實了我們上面的推斷,第1次迴圈時公式中的第n個數為預設常數;當第二次迴圈時,第n個數為391098894,隨後不斷變化的第n個數作為因子參與計算,這保證了結果的隨機性。
雖然通過我們的隨機數看起來也很隨機了,但必定這個演算法是偽隨機數,當第n個數和因子都相同時,生成的隨機數仍然是重複的隨機數,由於random提供乙個帶參的建構函式允許我們傳入乙個因子,如果傳入的因子隨機性強的話,那麼生成的隨機數也會比較可靠,為了提供乙個可靠點的因子,我們通常使用guid產生填充因子,同樣放在迴圈中測試:
for (int i = 0; i < 5; i++)
這樣的方式保證了填充因子的隨機性,所以生成的隨機數也比較可靠,執行結果如下所示:
734397360 1712793171 1984332878 819811856 1015979983
在一些場景下這樣的隨機數並不可靠,為了生成更加可靠的隨機數,微軟在system.security.cryptography命名空間下提供乙個名為rngcryptoserviceprovider的類,它採用系統當前的硬體資訊、程序資訊、執行緒資訊、系統啟動時間和當前精確時間作為填充因子,通過更好的演算法生成高質量的隨機數,它的使用方法如下所示:
byte randombytes = new byte[4]; rngcryptoserviceprovider rngserviceprovider = new rngcryptoserviceprovider(); rngserviceprovider.getbytes(randombytes); int32 result = bitconverter.toint32(randombytes, 0);
通過這種演算法生成的隨機數,經過成千上萬次的測試,並未發現重複,質量的確比random高了很多。另外windows api也提供了乙個非託管的隨機數生成函式cryptgenrandom,cryptgenrandom與rngcryptoserviceprovider的原理類似,採用c++編寫,如果要在.net中使用,需要進行簡單的封裝。它的原型如下所示:
bool winapi cryptgenrandom( _in_ hcryptprov hprov, _in_ dword dwlen, _inout_ byte *pbbuffer );
以上就是零度為您帶來的隨機數生成方法和基本原理,您可以通過需求和場景選擇最佳的方式,random演算法簡單,效能較高,適用於隨機性要求不高的情況,由於rngcryptoserviceprovider在生成期間需要查詢上面提到的幾種系統因子,所以效能稍弱於random類,但隨機數質量高,可靠性更好。
二.guid 類
system.guid
guid (globally unique identifier) 全球唯一識別符號
guid的計算使用到了很多在本機可取到的數字,如硬體的id碼,當前時間等.所計算出的128位整數(16位元組)可以接近唯一的輸出.
計算結果是******xx-***x-***x-***x-************結構的16進製制數字.當然這個格式也是可以更改的.
三.rngcryptoserviceprovider 類
system.security.cryptography.rngcryptoserviceprovider
rngcryptoserviceprovider 使用加密服務提供程式 (csp) 提供的實現來實現加密隨機數生成器 (rng)
因該類使用更嚴密的演算法.所以即使如下放在迴圈中,所計算出的隨機數也是不同的.
membership.generatepassword()
membership是乙個方便快捷的進行角色許可權管理的類,偶然發現乙個很有意思的方法,沒研究過是如何實現的
例:結果為
c!&^hotnv3!zhkk9babu
azlger)jj-uw8q*14yz*
i3qnb]zxu16ht!kkz!q*
9u:maq&c1x)^aed@xe**
ol(%4jvfbp&t5*hpl4l-
6@zj$cnhw&d+|xof:qik
a/!di&l*ty$qamh0gyzy
z^wu6{1bmq7d^+wu]>f$
1ogijs3&09fw0f9.|axa
8f+gy+l{o6x{sfugme*%
C 隨機數生成
using system using system.collections.generic using system.text namespace createrandomno return sb.tostring 生成大寫字母隨機數 public static string getabcpwd i...
C 隨機數生成
標準庫 被包含於中 提供兩個幫助生成偽隨機數的函式 函式一 int rand void 從srand seed 中指定的seed開始,返回乙個 seed,rand max 0x7fff 間的隨機整數。函式二 void srand unsigned seed 引數seed是rand 的種子,用來初始化...
C 隨機數生成
標準庫 被包含於中 提供兩個幫助生成偽隨機數的函式 函式一 int rand void 從srand seed 中指定的seed開始,返回乙個 seed,rand max 0x7fff 間的隨機整數。函式二 void srand unsigned seed 引數seed是rand 的種子,用來初始化...