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

打亂數(shù)組中的元素

問(wèn)題

你想打亂數(shù)組中的元素。

解決方案

Fisher-Yates shuffle 是一種高效、公正的方式來(lái)讓數(shù)組中的元素隨機(jī)化。這是一個(gè)相當(dāng)簡(jiǎn)單的方法:在列表的結(jié)尾處開(kāi)始,用一個(gè)隨機(jī)元素交換最后一個(gè)元素列表中的最后一個(gè)元素。繼續(xù)下一個(gè)并重復(fù)操作,直到你到達(dá)列表的起始端,最終列表中所有的元素都已打亂。這 [ Fisher-Yates shuffle Visualization ]( http://bost.ocks.org/mike/shuffle/) 可以幫助你理解算法。

shuffle = (source) ->
  # Arrays with < 2 elements do not shuffle well. Instead make it a noop.
  return source unless source.length >= 2
  # From the end of the list to the beginning, pick element `index`.
  for index in [source.length-1..1]
    # Choose random element `randomIndex` to the front of `index` to swap with.
    randomIndex = Math.floor Math.random() * (index + 1)
    # Swap `randomIndex` with `index`, using destructured assignment
    [source[index], source[randomIndex]] = [source[randomIndex], source[index]]
  source

shuffle([1..9])
# => [ 3, 1, 5, 6, 4, 8, 2, 9, 7 ]

討論

一種錯(cuò)誤的方式

有一個(gè)很常見(jiàn)但是錯(cuò)誤的打亂數(shù)組的方式:通過(guò)隨機(jī)數(shù)。

shuffle = (a) -> a.sort -> 0.5 - Math.random()

如果你做了一個(gè)隨機(jī)的排序,你應(yīng)該得到一個(gè)序列隨機(jī)的順序,對(duì)吧?甚至微軟也用這種隨機(jī)排序算法 。原來(lái),[這種隨機(jī)排序算法產(chǎn)生有偏差的結(jié)果]( http://blog.codinghorror.com/the-danger-of-naivete/) ,因?yàn)樗嬖谝环N洗牌的錯(cuò)覺(jué)。隨機(jī)排序不會(huì)導(dǎo)致一個(gè)工整的洗牌,它會(huì)導(dǎo)致序列排序質(zhì)量的參差不齊。

速度和空間的優(yōu)化

以上的解決方案處理速度是不一樣的。該列表,當(dāng)轉(zhuǎn)換成 JavaScript 時(shí),比它要復(fù)雜得多,變性分配比處理裸變量的速度要慢得多。以下代碼并不完善,并且需要更多的源代碼空間 … 但會(huì)編譯量更小,運(yùn)行更快:

shuffle = (a) ->
  i = a.length
  while --i > 0
    j = ~~(Math.random() * (i + 1)) # ~~ is a common optimization for Math.floor
    t = a[j]
    a[j] = a[i]
    a[i] = t
  a

擴(kuò)展 Javascript 來(lái)包含亂序數(shù)組

下面的代碼將亂序功能添加到數(shù)組原型中,這意味著你可以在任何希望的數(shù)組中運(yùn)行它,并以更直接的方式來(lái)運(yùn)行它。

Array::shuffle ?= ->
  if @length > 1 then for i in [@length-1..1]
    j = Math.floor Math.random() * (i + 1)
    [@[i], @[j]] = [@[j], @[i]]
  this

[1..9].shuffle()
# => [ 3, 1, 5, 6, 4, 8, 2, 9, 7 ]

注意: 雖然它像在 Ruby 語(yǔ)言中相當(dāng)普遍,但是在 JavaScript 中擴(kuò)展本地對(duì)象通常被認(rèn)為是不太好的做法 ( 參考: Maintainable JavaScript: Don’t modify objects you don’t own
正如提到的,以上的代碼的添加是十分安全的。它僅僅需要添 Array :: shuffle 如果它不存在,就要添加賦值運(yùn)算符 (? =) 。這樣,我們就不會(huì)重寫(xiě)到別人的代碼,或是本地瀏覽器的方式。

同時(shí),如果你認(rèn)為你會(huì)使用很多的實(shí)用功能,可以考慮使用一個(gè)工具庫(kù),像 Lo-dash 。他們有很多功能,像跨瀏覽器的簡(jiǎn)潔高效的地圖。 Underscore 也是一個(gè)不錯(cuò)的選擇。