用 C 呼叫 rand() 每次都回傳相同結果?

為了寫一個隨機產生 ID 的程式,我用C去呼叫 rand() 這亂數產生函式,然而經過測試後發現,每次程式執行所產生的亂數都是一樣的。這問題讓我花了點時間研究了一下,我發現並非每次執行都一樣,而是每秒內產生 出來的亂數是一樣的。很明顯的,這裡的亂數種子設定出了點問題。

在過去習慣上,在使用亂數前 我們會用 srand(time(0)) 設定亂數種子,而一般的書籍也是這樣教我們。但這種方式設定亂數種子,種子的變化是以秒為單位,也就是每秒只變動一次。由於種子在一秒鐘之內都是一樣的, 每次使用 rand() 時,產生出來的亂數也都會是相同的。


這樣設定亂數種子其實 並沒有錯,因為一般程式只需要在開頭設定第一次,之後就不必再設。但如果我們要寫的是一個指令,使用者就可能在一秒鐘之內重覆執行你的程式很多遍。那將會 導致亂數種子被重新定義很多遍。每當種子被重新設定,rand()自然就會把亂數表重頭讀取,當然,每次所得到的亂數就會相同。


我暫時想出了個解決辦法,就是針對種子再給予時間以外的變數,讓每次執行的種子變得更不一樣。於是我將種子設定改成這樣:

srand(time(0)+getpid());


這 以程式執行時的行程處理ID加了進來當種子,因為每個程式的 PID 一定不一樣,如果要一樣也要等許多的程式執行後,重頭分配 PID 才有可能相同。以 PID 再加上原本每秒產生出來的時間,如此產生出來的種子必定獨一無二。這點小技巧能夠解決 rand() 每次回傳都相同的問題。

註:本文是架構在 Linux 上,並以 gcc 編譯所產生的程式當基準,若在 Windows 等其他平台和以 VC++ 、Turbo C等其他款編譯器,不能保證本文所談到的東西能起任何作用。

留言

  1. srand(getpid()) 就夠了
    否則 time(0)+getpid() 還是可能相同的
    如果還要考慮不同機器產生的亂數,找 UUID 的邏輯來看看吧

    http://en.wikipedia.org/wiki/UUID

    回覆刪除
  2. 若是只用 srand(getpid()),亂數變化太少。
    假設系統限制 65535 個 process,就只有 65535 個亂數表。
    如果我們又只取開頭幾位數,就有 65535 個亂數,重覆性太高。
    所以才用『已經歷的秒數』+『PID』來解決。

    關於 UUID ,我已經筆記下來了,謝謝你的資料提供。 :)

    回覆刪除
  3. 如果你的程式是這樣用法,至少應該改成用 */ 而非 +- 會比較好
    而實際上,如果會被這麼頻繁用到
    用 /dev/random 或一隻獨立的 daemon 負責提供亂數會是比較好的作法(如果你擔心被人逆推或猜出 seed,獨立 daemon 中還是可以設計成不定時切換 seed)

    回覆刪除
  4. 同意您所說的,使用 /dev/random 的確是比較好的解決方法。 :)

    回覆刪除

張貼留言

這個網誌中的熱門文章

有趣的邏輯問題:是誰在說謊

Web 技術中的 Session 是什麼?

淺談 USB 通訊架構之定義(一)

淺談 USB 通訊架構之定義(二)

Reverse SSH Tunnel 反向打洞實錄