TIPS-避免連續快速建立Random物件

這是在玩三門問題時程式沒寫好遇到的狀況,做個筆記。

以下程式會連續建立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);
        }
    }
歡迎推文分享:
Published 10 September 2010 09:19 AM 由 Jeffrey
Filed under: ,
Views: 9,870



意見

# Jeff Yeh said on 10 September, 2010 01:05 AM

用蹂躪的這招不錯,用GUID當種子~

www.dotblogs.com.tw/.../12801.aspx

# Jeffrey said on 10 September, 2010 02:15 AM

to Jeff Yeh, 哈! 我最先想到的也是用Guid當種子,後來覺得一直重複建立Guid(),轉換成int,再拿來建立Random()有點"搞剛"。後來發現在我的案例中,將Random物件宣告成靜態後重複使用應該更有效率。

不過,如果想徹底切斷亂數間的關聯性,用Guid()應是不錯的選擇。

# Wayne said on 13 September, 2010 12:54 AM

使用 lock(rnd) {...} 會不會比較好?

# Jeffrey said on 13 September, 2010 08:42 AM

to Wayne, 在本例中沒有啟動多執行緒,我想應該用不上lock,還是你另有其他點子? 願聞其詳~~ :P

# WORM said on 13 September, 2010 08:23 PM

這因該會比較亂

msdn.microsoft.com/.../system.security.cryptography.rngcryptoserviceprovider(VS.80).aspx

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<September 2010>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication