我正在通过阅读“ JAVA,如何编程”一书来学习Java,我已经读到了第7章,它讨论了数组和数组操作。在7.5节(案例研究:纸牌洗牌和交易模拟)中,它介绍了一个程序,该程序创建一副纸牌,对其进行洗牌,并显示经过洗牌的纸牌,而没有任何重复。
在程序中,它使用shuffle方法在创建卡片后对其进行洗牌。该方法使用随机对象创建一个介于0到51之间的随机数,以从卡片组中选择一张卡片并将其分配给一个数组。代码:
public void shuffle() {
// after shuffling, dealing should start at deck[ 0 ] again
currentCard = 0; // reinitialize currentCard
// for each Card, pick another random Card (0-51) and swap them
for (int first = 0; first < deck.length; first++) {
// select a random number between 0 and 51
int second = randomNumbers.nextInt( NUMBER_OF_CARDS );
// swap current Card with randomly selected Card
Card temp = deck[ first ];
deck[ first ] = deck[ second ];
deck[ second ] = temp;
}
}
如果int second = randomNumbers.nextInt( Number_OF_CARDS );
该怎么办
生成一个已经选择的随机数?甲板上不会有重复的东西吗?如果不是这种情况,为什么此行没有给出重复的数字?我想念什么?
我了解到,在给定的范围内,“几乎”均等地生成了随机数,但是仍有机会生成之前已经生成的数字。
答案 0 :(得分:2)
它不会重复,因为您总是在交换卡,并且交换也永远不会重复。例如,假设我的列表以[a, b, c, d]
开始。如果我选择1作为我的第一个随机数,则我交换元素0和2,最后得到[c, b, a, d]
。如果我再次选择2作为第二个数字,则交换1和2,最后得到[c, a, b, d]
。
但是您是是正确的,该算法是错误的!您可以交换已交换的卡,这意味着较早交换的卡更有可能再次被交换,这会带来偏差。 This CodingHorror blog post对此进行了讨论,如果您搜索“错误的改组算法”之类的话,在线上还有许多其他讨论。
相反,如果您尝试将卡交换到位置i
,则应该从位置x
随机选择一张卡,例如i <= x < deck.length
。我暂时忘了这本书的(不正确的)算法的名字,但是我提到的变种叫做Fisher–Yates shuffle。
答案 1 :(得分:0)
此代码不会从卡组中挑选卡片并插入新列表。而是多次交换两张随机卡。卡组内的卡仍然彼此“唯一”。多次选择一个索引不是问题,它只是与另一张卡交换了。因此,在一种情况下,它可能会在索引5
和14
处交换卡,而在另一次for
循环迭代中,它可能会在索引11
和{{1}处交换卡。 }。但是最后,卡片组包含了相同的卡片,但是它们不再是原始顺序了。
答案 2 :(得分:0)
这是我多年前学到的算法。关键是要不断减少随机数的限制,这样您就不会替换已替换的内容。
public class ShuffleDemo {
int max = 10;
public static void main(String[] args) {
new ShuffleDemo().shuffle();
}
public void shuffle() {
// start off with an array of 1 to max
int[] vals = IntStream.rangeClosed(0, max).toArray();
while (max > 0) {
int selection = ThreadLocalRandom.current().nextInt(max);
int temp = vals[max - 1];
vals[max - 1] = vals[selection];
vals[selection] = temp;
max--;
}
System.out.println(Arrays.toString(vals));
} // end method
}
卡仍然有可能最终回到其原始位置。但是,那也可以在真正的套牌中发生