亂數兩三事
是亂數兩三事,也是亂述兩三事[1]。剛好在 Plurk 上聊到亂數的問題:我印象中 Standard C 的 srand()、rand() 函式,有大問題,所以在 Plurk 上亂放炮,引來眾家好手關切。還好最後把細節回憶清楚了,也再次提醒了自己,要小心不要讓寫程式的「習慣」埋下難解的地雷。
故事是這樣的,在之前開發某分散式系統時,為了避免系統啟動時,因為散布在多台機器的許多程式,一起同時運作而造成塞車,所以套用 CSMA/CD 的招數,讓大家在啟動時,各自暫停若干時間,暫停的長度由亂數決定。
不過,因為 Standard C 的 srand()、rand() 係 pseudo random 的關係,只要給 srand() 的值一樣,之後大家所 rand() 得到的 sequence 都會一樣。大部份的教科書,在介紹這兩個函式的時候,大都是用 time() 取得當前 epoch 秒數喂進 srand(),所以如訓練有術的狗一般,我也很直覺的就用同一個方法設定亂數種子值。然後很不幸地,當所有程式同時啟動時,因為呼叫 time() 的時間非常接近,所以傳進 srand() 的數值,也幾乎一模一樣,導至這整招失效。而且因為 random sequence 一模一樣,所以後面的動作都非常「synchronize」,效率可想而知。
我那時的解法是,乾脆進而撰寫了 device_random_generator 與 guid_random_generator 來使用,前者直接利用硬體手法取得亂數值,後者仰賴了 GUID 的產生,有一部份是亂數的結果[2]。
但若嫌麻煩,使用 avhacker 或 sorry 提的,用更精確的時間,或把 Process ID、Thread ID 等一起參作伙做撒尿魚丸,來得到亂數種子值,都是簡便但可行的好方法。



2 Comments
我以前常用allocate一塊比較大的記憶體(>1MB)然後free掉的方法,取得的記憶體位置我目測是還蠻random的,不過我通常還是會加上時間函數,跟原本的random值乘起來,像是time(),或是gettimeofday之類的。
大家的方法都很好,重點是不要學我偷懶,要不然會中招。:-p
Post a Comment