TIPS-避免連續快速建立Random物件
5 |
這是在玩三門問題時程式沒寫好遇到的狀況,做個筆記。
以下程式會連續建立1000個Test物件,Test物件建構式中會產生A, B, C三個隨機亂數。大家有發現其中存在什麼問題嗎?
using System;
namespace TestRandom
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 1000; i++)
{
Test t = new Test();
Console.WriteLine(
"NO:{0:000} A:{1:00} B:{2:00} C:{3:00}",
i, t.A, t.B, t.C);
}
Console.Read();
}
}
public class Test
{
public int A, B, C;
Random rnd = new Random();
public Test()
{
A = rnd.Next(100);
B = rnd.Next(100);
C = rnd.Next(100);
}
}
}
實地執行一下程式,你會發現亂數怎麼一點都不亂,例如以下結果:
NO:978 A:66 B:39 C:77
NO:979 A:66 B:39 C:77
NO:980 A:66 B:39 C:77
NO:981 A:66 B:39 C:77
NO:982 A:66 B:39 C:77
NO:983 A:66 B:39 C:77
NO:984 A:66 B:39 C:77
NO:985 A:66 B:39 C:77
NO:986 A:49 B:79 C:25
NO:987 A:49 B:79 C:25
NO:988 A:49 B:79 C:25
NO:989 A:49 B:79 C:25
NO:990 A:49 B:79 C:25
NO:991 A:49 B:79 C:25
NO:992 A:49 B:79 C:25
NO:993 A:49 B:79 C:25
NO:994 A:49 B:79 C:25
NO:995 A:49 B:79 C:25
NO:996 A:49 B:79 C:25
NO:997 A:49 B:79 C:25
NO:998 A:49 B:79 C:25
NO:999 A:49 B:79 C:25
我們會得到連續N組A, B, C亂數相同的結果,一陣子後變成另外一組亂數再重複N次,再隔一陣子後再換成另一組亂數重複N次... 問題出在亂數種子! MSDN中有詳細說明:
亂數的產生始於種子值。如果重複使用相同的種子會產生相同的連續數字。其中一個產生不同序列的方法是讓種子值時間相依,由此以每個 Random 的新執行個體 (Instance) 產生不同的系列。根據預設,Random 類別的無參數建構函式會使用系統時鐘來產生其種子值,而參數化的建構函式可以根據目前時間的刻度數目而接受 Int32 值。然而,因為時鐘的解析度有限,所以若使用無參數的建構函式在極短時間內連續建立不同的 Random 物件,就會建立亂數產生器,這些產生器會產生序列完全相同的亂數。
所以祕訣在於:
不要快速地連續new Random(),應該建立一個Random後重複使用。
以先前程式為例,最簡單的修改方式是把rnd宣告成靜態變數,全部的Test物件共用一份,就能解決問題囉!
public class Test
{
public int A, B, C;
static Random rnd = new Random();
public Test()
{
A = rnd.Next(100);
B = rnd.Next(100);
C = rnd.Next(100);
}
}
Comments
# by Jeff Yeh
用蹂躪的這招不錯,用GUID當種子~ http://www.dotblogs.com.tw/larrynung/archive/2010/01/04/12801.aspx
# by Jeffrey
to Jeff Yeh, 哈! 我最先想到的也是用Guid當種子,後來覺得一直重複建立Guid(),轉換成int,再拿來建立Random()有點"搞剛"。後來發現在我的案例中,將Random物件宣告成靜態後重複使用應該更有效率。 不過,如果想徹底切斷亂數間的關聯性,用Guid()應是不錯的選擇。
# by Wayne
使用 lock(rnd) {...} 會不會比較好?
# by Jeffrey
to Wayne, 在本例中沒有啟動多執行緒,我想應該用不上lock,還是你另有其他點子? 願聞其詳~~ :P
# by WORM
這因該會比較亂 http://msdn.microsoft.com/zh-tw/library/system.security.cryptography.rngcryptoserviceprovider(VS.80).aspx