我被要求编写一个程序(主要是一种方法)来改组一副牌。我写了以下程序:
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
但这对我没有意义。
答案 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}
后将其返回。
你应该能够看到这是如何发展的。无需担心预先对整个列表进行排序,您可以实现随机序列(好,随机数生成器允许的随机序列),无需重复。