首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Fisher-Yates混洗算法在javascript,现代和内-外版本

Fisher-Yates混洗算法在javascript,现代和内-外版本
EN

Code Review用户
提问于 2012-02-08 23:41:26
回答 2查看 4.4K关注 0票数 5

我在javascript中实现了两个不同版本的费舍-耶茨洗牌,我想知道我是否犯了任何错误。

我在创造一副扑克牌。我对这些洗牌算法很感兴趣。

传递给下面的rng参数的对象是约翰·巴格的“阿利亚”的包装器,它是一个可播种的PRNG,它应该产生比内置Math.random更好的随机数样本。它的API与Math.random的API相同。

我主要想知道我是否犯了任何错误或误解了我所引用的例子。

代码语言:javascript
复制
/** shuffle

    Shuffle an array. 

    @see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm

    @param {Array} array
    @param {Function} random Optional RNG. Defaults to Math.random.
    @return {Array} The original array, shuffled.
*/
function shuffle (array, random) {
  var i = array.length, j, swap;
  while (--i) {
    j = (random ? random() : Math.random()) * (i + 1) | 0;
    swap = array[i];
    array[i] = array[j];
    array[j] = swap;
  }
  return array;
}

/** pushRand

    Insert a value into an array at a random index. The element 
    previously at that index will be pushed back onto the end. 

    @see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm

    @param {Array} object to shuffle.
    @param {Mixed} value to insert.
    @param {Function} optional RNG. Defaults to Math.random.
    @return {Number} The new length of the array.

*/
function pushRand (array, value, random) {
  var j = (random ? random() : Math.random()) * array.length | 0;
  array.push(array[j]);
  array[j] = value;
  return array.length;
}

这是洗牌的演示。重新加载页面可以进行内退洗牌,而点击正面向下的卡片可以完成现代洗牌。

编辑:我在“现代”版本中发现了一个fencepost错误,并在这里修复了它,但它仍然在演示中。还不确定“内-出”版本。

编辑:我不再懒惰了,把这些东西都考虑了出来,所以现在应该更容易看了。新的演示。

编辑:内-外程序也有fencepost错误。应该是这样的,我想:

代码语言:javascript
复制
function pushRand (array, value, random) {
  var j = (random ? random() : Math.random()) * (array.length + 1) | 0;
  array.push(array[j]);
  array[j] = value;
  return array.length;
}

谢谢保罗!

编辑:考虑到Adam的优化建议,新代码如下所示:

代码语言:javascript
复制
/** shuffle

    Shuffle an array. 

    @see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm

    @param {Array} array Array to shuffle.
    @param {Object} rng Optional RNG. Defaults to Math.
    @return {Array} The original array, shuffled.
*/
function shuffle (array, rng) {
  var i = array.length, j, swap;
  if (!rng) rng = Math;
  while (--i) {
    j = rng.random() * (i + 1) | 0;
    swap = array[i];
    array[i] = array[j];
    array[j] = swap;
  }
  return array;
}

/** pushRand

    Insert a value into an array at a random index. The element 
    previously at that index will be pushed back onto the end. 

    @see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm

    @param {Array} array Array to insert into.
    @param {Mixed} value Value to insert.
    @param {Object} rng Optional RNG. Defaults to Math.
    @return {Number} The new length of the array.

*/
function pushRand (array, value, rng) {
  var j = (rng || Math).random() * (array.length + 1) | 0;
  array.push(array[j]);
  array[j] = value;
  return array.length;
}

下面是更新的可编辑演示 (底部的相关代码)。

EN

回答 2

Code Review用户

回答已采纳

发布于 2012-02-09 02:36:52

除非您修改内到外代码以通过(index+1)进行缩放

代码语言:javascript
复制
       randomIndex = rand()*(index+1)|0;

你不会涵盖所有的可能性。对于index == 0,您总是生成0 --这是正确的,但只是偶然的。

对于index == 1,您也总是生成0 --而不是0到1之间的50/50分隔。

甲板上的第二张牌错过了成为card[1]的唯一机会。

从那时起,对于每一张卡,同样的问题还会继续,如果您需要+1randomIndex < index,则不需要randomIndex <= index

对于最后一张放在甲板上的牌,问题可能会更清楚。对于52张牌,index == 51,所以randomIndex最多可以是50张。因此,任何先前的卡都有可能在卡51结束,但不是最后一张卡。它不能得到接近卡50。

值得欣慰的是,这一变化使得最初洗牌的数学看起来更像重新洗牌。

票数 1
EN

Code Review用户

发布于 2012-02-09 06:27:29

请注意,您可以优化while循环中的第一行:

代码语言:javascript
复制
 function shuffle (array, random) {
    var i = array.length, j, swap;
    while (--i) {
      j = (random ? random() : Math.random()) * (i + 1) | 0;

对随机的布尔值的检查每次都会发生(根据编译器的不同,我很好地考虑了围绕它的一些优化)。它可能会像这样跑得更快一点:

代码语言:javascript
复制
 function shuffle (array, random) {
    var i = array.length, j, swap;
    random = random || Math.random;
    while (--i) {
      j = random() * (i + 1) | 0;

编辑:有错误的random |= (执行二进制或)-替换为random = random ||

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/8793

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档