用Javascript和不一致的数组值洗牌?

时间:2015-04-26 23:46:29

标签: javascript arrays race-condition

我在javascript中构建一个小模块,就像一包卡片一样。我的第一种方法很有效,但很简单,所以我想创建一些模拟真实世界卡改组背后的想法的随机方法。

在其他一些有用的功能中,我创建了riffle,overhand和cut功能,所有这些功能似乎都在那里工作,但是当按顺序重复调用它们时,返回的包装数量是不一致的,一次又一次地运行它它似乎是某种竞争条件,但似乎无法理解如何避免它。

相关的私人方法是:

riffle : function riffle() {
        var top         = Pack.slice(0, 26);
        var bottom = Pack.slice(26, 52);
        Pack = [];
        console.log('top is '+top.length+" and bottom is "+bottom.length);
        var hand    = 'right';
        var result = [];
        var i = 52;

        while (i > 0) {
            var drop    = Math.floor(Math.random()*3)+1;
            var cards;



            if (hand === 'right' ) {
                if (drop >= top.length) {
                    cards = top;
                } else {
                    cards = top.splice(0, drop);
                }
                hand = 'left';

            } else {
                if (drop >= bottom.length) {
                    cards = bottom;
                } else {
                    cards = bottom.splice(0, drop);
                }
                hand = 'right';
            }

            result = result.concat(cards);
            i -= drop;

        }
        Pack = result;
        console.log(Pack.length+" after riffle");
        return this;
    },

 cut : function cut(fn) {
        var top         = Pack.slice(0, 26);
        var bottom = Pack.slice(26, 52);
        Pack = [];
        console.log(top);
        Pack = bottom.concat(top);
        console.log(Pack.length+" after cut");
        if (fn && typeof(fn) === 'function') { fn(); }
        return this;
    }

后来我有一个名为shuffle的特权方法,它调用它们:

    shuffle   : function shuffle(cb) {
        State.cardsOut = [];
        Internal.generatePack().cut().riffle().riffle()
                            .riffle().riffle().riffle();

        if (cb && typeof(cb) === 'function') { cb(); }
    }

注意:我从一个生成函数开始,该函数创建一个对象的arrray,代表一整套52张牌。当我在洗牌和削减后的不同时间登录包时,我得到的结果各不相同,我似乎无法弄清楚原因。

你可以看到我在这里工作的公里

https://gist.github.com/Pushplaybang/66bc7a1fa5d84eee2236

任何帮助都会很棒。

2 个答案:

答案 0 :(得分:1)

drop变量存储了你应该从左手或右手翻转的牌数。但是,有两种情况:

if (drop >= top.length) {
    cards = top;
}

if (drop >= bottom.length) {
    cards = bottom;
}

其中drop可能大于包裹中一半剩余卡片的数量,因此从i中减去的卡数将比您实际上减去的数量更多。您可以通过以下方式解决此问题:

if (drop >= top.length) {
    drop  = top.length;
    cards = top;
    top   = [];
}

if (drop >= bottom.length) {
    drop   = top.length;
    cards  = bottom;
    bottom = [];
}

(您需要清空阵列,否则最终可能会添加两次相同的卡片。)

其他问题

  • 你在代码(2652中有魔术数字)这些可以是在类中定义的常量并给出适当的名称(即PACK_SIZE = 52),这意味着如果你创建一个代表不同数量卡片的子类,它仍可以使用。
  • hand有两个可能的值,可以表示为布尔值,但是你可以为它分配字符串(同样可以使用常量LEFT_HAND = true, RIGHT_HAND = !LEFT_HAND)。
  • Pack似乎是一个全局变量 - 我原以为它应该是该类的成员。
  • 您不需要命名函数,因为这只会污染全局命名空间:riffle : function riffle() {只能是一个匿名函数riffle : function() {
  • 性能 - 每次迭代都会创建其他数组,并且会多次移动卡片。这可能更有效率。

这样的事情:

PACK_SIZE: 52,
riffle : function() {
  var index_of_cards_riffled_from_top = 0;
  var index_of_cards_riffled_from_bottom = this.PACK_SIZE / 2;
  var riffled_cards = [];
  while ( index_of_cards_riffled_from_top < this.PACK_SIZE / 2
         || index_of_cards_riffled_from_bottom < this.PACK_SIZE ) {
    var num_cards_to_riffle_top = Math.min( this.PACK_SIZE / 2 - index_of_cards_riffled_from_top, Math.floor( Math.random() * 3 ) + 1 );
    var num_cards_to_riffle_bottom = Math.min( this.PACK_SIZE - index_of_cards_riffled_from_bottom, Math.floor( Math.random() * 3 ) + 1 );
    while ( num_cards_to_riffle_top > 0 ) {
      riffled_cards.push( this.Pack[ index_of_cards_riffled_from_top++ ] );
      num_cards_to_riffle_top--;
    }
    while ( num_cards_to_riffle_bottom > 0 ) {
      riffled_cards.push( this.Pack[ index_of_cards_riffled_from_bottom++ ] );
      num_cards_to_riffle_bottom--;
    }
  }
  this.Pack = riffled_cards;
}

答案 1 :(得分:0)

虽然@MTO的答案确实解决了我的问题,但我想了解一下我如何选择开始重构这个功能。

    riffle : function riffle() {
        var cutPos = Math.floor(Math.random()*rv)+( (cardCount-rv) / 2 );
        var splitPack = {
            left : Pack.splice(0, cutPos),
            right : Pack.splice(0, Pack.length)
        };

        var hand    = 'right',result = [], i = 52, cards;

        while(i > 0) {
            drop    = Math.floor(Math.random()*3)+1;

            if (drop >= splitPack[ hand ].length) {
                drop = splitPack[ hand ].length;
            } 

            cards = splitPack[ hand ].splice(0, drop);
            hand = (hand === 'left') ? 'right' : 'left';

            result = result.concat(cards);
            cards = [];
            i -= drop;

        }

        Pack = result;
        console.log(Pack.length+" after riffle");
        return this;
    },

一些事情:

  • 看似全局的元素并不是真的,因为这些都包含在一个创建一个新的&#34; deck&#34;对象,并且一些元素需要是私有的,例如一旦交易开始就保留在包中的卡片。
  • 虽然布尔语对于双手来说效果很好,但我想稍微将其煮沸,所以使用字符串来选择obj属性。
  • MTO关于使用常量的所有内容绝对有效。
  • 现在每次拼接,我们都会从数组中删除元素。
  • 我更喜欢这种方法,因为它只使用一个while循环。
  • 最后,这种类型的洗牌是为了模仿手洗牌,并且必须与其他手洗牌方法相结合,理想情况下是重复序列,以产生有用的东西,
  • 如果你想要一致的随机和有效使用fischer-yates算法。