你需要生成在一定范圍內(nèi)的隨機數(shù),但你也需要對發(fā)生器進行“生成種子”操作來提供可預測的值。
編寫你自己的隨機數(shù)生成器。當然有很多方法可以做到這一點,這里給出一個簡單的示例。 該發(fā)生器絕對不可以以加密為目的!
class Rand
# 如果沒有種子創(chuàng)建,使用當前時間作為種子
constructor: (@seed) ->
# Knuth and Lewis' improvements to Park and Miller's LCPRNG
@multiplier = 1664525
@modulo = 4294967296 # 2**32-1;
@offset = 1013904223
unless @seed? && 0 <= seed < @modulo
@seed = (new Date().valueOf() * new Date().getMilliseconds()) % @modulo
# 設(shè)置新的種子值
seed: (seed) ->
@seed = seed
# 返回一個隨機整數(shù)滿足 0 <= n < @modulo
randn: ->
# new_seed = (a * seed + c) % m
@seed = (@multiplier*@seed + @offset) % @modulo
# 返回一個隨機浮點滿足 0 <= f < 1.0
randf: ->
this.randn() / @modulo
# 返回一個隨機的整數(shù)滿足 0 <= f < n
rand: (n) ->
Math.floor(this.randf() * n)
#返回一個隨機的整數(shù)滿足min <= f < max
rand2: (min, max) ->
min + this.rand(max-min)
JavaScript 和 CoffeeScript 都不提供可產(chǎn)生隨機數(shù)的發(fā)生器。編寫發(fā)生器對于我們來說將是一個挑戰(zhàn),在于權(quán)衡量的隨機性與發(fā)生器的簡單性。對隨機性的全面討論已超出了本書的范圍。如需進一步閱讀,可參考 Donald Kunth 的 The Art of Computer Programming 第 Ⅱ 卷第 3 章的 “ Random Numbers ” ,以及 Numerical Recipes in C 第二版本第 7 章的“ Random Numbers ”。
但是,對于這個隨機數(shù)發(fā)生器只有簡單的解釋。這是一個線性同余偽隨機數(shù)發(fā)生器,其運行源于一條數(shù)學公式 Ij+1 = (aIj+c) % m,其中 a 是乘數(shù),c 是加法偏移量,m 是模數(shù)。每次請求隨機數(shù)時就會執(zhí)行很大的乘法和加法運算——這里的“很大”與密鑰空間有關(guān)——得到的結(jié)果將以模數(shù)的形式被返回密鑰空間。
這個發(fā)生器的周期為 232。雖然它絕對不能以加密為目的,但是對于最簡單的隨機性要求來說,它是相當足夠的。randn() 在循環(huán)之前將遍歷整個密鑰空間,下一個數(shù)由上一個來確定。
如果你想修補這個發(fā)生器,強烈建議你去閱讀 Knuth 的 The Art of Computer Programming 中的第 3 章。隨機數(shù)生成是件很容易弄糟的事情,然而 Knuth 會解釋如何區(qū)分好的和壞的隨機數(shù)生成。
不要把發(fā)生器的輸出結(jié)果變成模數(shù)。如果你需要一個整數(shù)的范圍,應使用分割的方法。線性同余發(fā)生器的低位是不具有隨機性的。特別的是,它總是從偶數(shù)種子產(chǎn)生奇數(shù),反之亦然。所以如果你需要一個隨機的 0 或者 1,不要使用:
# NOT random! Do not do this!
r.randn() % 2
因為你肯定得不到隨機數(shù)字。反而,你應該使用 r.rand(2)。