鍍金池/ 教程/ HTML/ 生成可預測的隨機數(shù)
備忘錄模式
解釋器模式
類似 Python 的 zip 函數(shù)
類變量和實例變量
提示參數(shù)
指數(shù)對數(shù)運算
檢查變量的類型是否為數(shù)組
由數(shù)組創(chuàng)建一個字符串
生成隨機數(shù)
刪除數(shù)組中的相同元素
大寫單詞首字母
雙向服務(wù)器
類的混合
計算復活節(jié)的日期
轉(zhuǎn)換弧度和度
找到上一個月(或下一個月)
雙向客戶端
橋接模式
嵌入 JavaScript
AJAX
觀察者模式
克隆對象(深度復制)
一個隨機整數(shù)函數(shù)
清理字符串前后的空白符
歸納數(shù)組
平方根倒數(shù)快速算法
適配器模式
打亂數(shù)組中的元素
將數(shù)組連接
使用數(shù)組來交換變量
更快的 Fibonacci 算法
服務(wù)器
服務(wù)端和客戶端的代碼重用
客戶端
查找子字符串
策略模式
CoffeeScrip 的 type 函數(shù)
由數(shù)組創(chuàng)建一個對象詞典
回調(diào)綁定
工廠方法模式
映射數(shù)組
當函數(shù)括號不可選
生成可預測的隨機數(shù)
不使用 jQuery 的 Ajax 請求
把字符串轉(zhuǎn)換為小寫形式
類方法和實例方法
擴展內(nèi)置對象
定義數(shù)組范圍
MongoDB
匹配字符串
創(chuàng)建一個不存在的對象字面值
列表推導
比較范圍
修飾模式
檢測每個元素
拆分字符串
字符串插值
對象數(shù)組
去抖動函數(shù)
使用 Nodeunit 測試
SQLite
單件模式
篩選數(shù)組
替換子字符串
數(shù)組最大值
計算(美國和加拿大的)感恩節(jié)日期
找到一個月中的最后一天
計算兩個日期中間的天數(shù)
基本的 HTTP 服務(wù)器
把字符串轉(zhuǎn)換為大寫形式
使用 HTML 命名實體替換 HTML 標簽
For 循環(huán)
模板方法模式
重復字符串
使用 Jasmine 測試
對象的鏈式調(diào)用
數(shù)學常數(shù)
反轉(zhuǎn)數(shù)組
計算月球的相位
使用 Heregexes
查找子字符串
生成器模式
遞歸函數(shù)
HTTP 客戶端
創(chuàng)建 jQuery 插件
檢測與構(gòu)建丟失的函數(shù)
生成唯一ID
命令模式

生成可預測的隨機數(shù)

問題

你需要生成在一定范圍內(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)。