在交换两个值后,洗牌一副卡,冗余

时间:2013-05-01 06:12:54

标签: java

我被要求编写一个程序(主要是一种方法)来改组一副牌。我写了以下程序:

public class Deck {

////////////////////////////////////////
// Data Members
////////////////////////////////////////

private Card[] cards; // array holding all 52 cards
private int cardsInDeck; // the current number of cards in the deck

public static final int DECK_SIZE = 52;

/**
 * Shuffles the deck (i.e. randomly reorders the cards in the deck). 
 */
public void shuffle() {
    int newI;
    Card temp;
    Random randIndex = new Random();

    for (int i = 0; i < cardsInDeck; i++) {

        // pick a random index between 0 and cardsInDeck - 1
        newI = randIndex.nextInt(cardsInDeck);

        // swap cards[i] and cards[newI]
        temp = cards[i];
        cards[i] = cards[newI];
        cards[newI] = temp;
    }
}

}

但是上面的shuffle方法存在逻辑错误,如下所示:假设我用卡号42替换卡号4,然后我交换 两次。我想知道有没有办法不这样做?

我在这里查了一篇帖子:Shuffling a deck of cards

但这对我没有意义。

3 个答案:

答案 0 :(得分:15)

  

我想知道有没有办法不这样做?

绝对。而不是使用任何其他卡交换一张卡,只需将一张卡与以后的交换一张。

所以在任何一点上,你真的从未被挑选的“剩下的所有牌”中选择哪张牌位于i位。概念上 等同于从一张卡片列表开始,并随机删除卡片以放置在新的洗牌集合中。事实上,当你正在进行交换时,你实际上是在交换位置是无关紧要的,因为在任何时候你都会从剩余的插槽中随机选择。

阅读Wikipedia article on the Fisher-Yates shuffle了解更多信息。

(某些实现从 end 交换,因此元素x[0, x]范围内的随机元素交换。这相当于我所描述的,只是镜像。就个人而言,我觉得更容易将集合的第一个部分视为任何时候的洗牌部分,但这是我的失败而不是固有的差异。)

另请注意,如果您使用List<Card>,则可以使用Collections.shuffle并避免为此编写代码。

答案 1 :(得分:7)

你可以将你的实现与Collections.shuffle进行比较,一个肯定是正确的,这是来自src的片段

  // Shuffle array
  for (int i=size; i > 1; i--)
      swap(arr, i-1, rnd.nextInt(i));

...

   private static void swap(Object[] arr, int i, int j) {
        Object tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

答案 2 :(得分:5)

有时候,洗牌的最好方法是洗牌。由于您已经知道如何使用随机数,因此可以使用Fisher-Yates shuffle的修改以随机顺序提取卡片,无需重复,无需初始排序。

用这些物理术语来思考它(当使用真正的牌组时)。而不是在前面洗牌,然后连续提取顶牌,只需按照分类顺序离开牌组,每次从随机位置提取一张牌。

请参阅here以获取有关其工作原理的完整说明,但我将介绍如下从1到9提取三个数字。


从(未检验的)列表{1,2,3,4,5,6,7,8,9}(显然长度为9)开始,并根据该长度生成一个随机数(从0到8,包括0到8,假设我们使用从零开始的索引,Java会这样做) 。假设第一个随机数是4。

然后将项目保存在位置编号4(即5)并将列表中的_last项目(9)移动到该位置,将长度减去1。这样,{1,2,3,4,9,6,7,8}的长度为8。

然后使用基于长度的随机数(包括0到7)再次返回第二个数字。在这种情况下,我们将获得随机数1。

偏移1处的项目为2,然后我们将调整列表与第一步相同,使{1,8,3,4,9,6,7}的长度为7.

现在假设我们根据当前长度7得到第三个随机数,它恰好是4。该项目现在为9,因此我们在将列表修改为长度为6的{1,8,3,4,7,6}后将其返回。

你应该能够看到这是如何发展的。无需担心预先对整个列表进行排序,您可以实现随机序列(好,随机数生成器允许的随机序列),无需重复。