c 隨機數生成編號 C 生成隨機數的三種方法

2021-10-13 11:36:25 字數 3758 閱讀 3829

隨機數的定義為:產生的所有數字毫無關係.

在實際應用中很多地方會用到隨機數,比如需要生成唯一的訂單號.

在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 的種子,用來初始化...