我正在创建一个简单的方法来模拟洗牌。我的想法是存储原始牌组的大小,并且只要大小不是负值就重复循环。
在循环中,我从列表中复制一个对象(卡),并将其放在另一个列表中。从原始列表中删除,然后恢复循环。
while(size >= 0){
int random = (int) ((Math.random() * size) + 0);
shuffledDeck.add(this.orderedDeck.get(random));
orderedDeck.remove(random);
size--;
}
这个特殊套牌的大小是
int size = this.getDeckSize()-1; //51 (52 cards, from 0 to 51)
我的问题是,在几次不同的尝试中,洗牌的牌组中的最后一张牌与未洗牌的牌组中的最后一张牌一致。这表明random
永远不会等于size-1
。
我怎样才能让最后一张卡实际上可以洗牌?
(换句话说,为什么random
等于size-1
?)
答案 0 :(得分:3)
Math.random()
永远不会返回1.0
,因为docs会解释:
返回带有正号的double值,大于或等于0.0且小于1.0。
即使它确实返回1.0
,它也会以极小的概率进行,并且您的代码会向下舍入任何其他值,因此您仍然无法统一获得所需的结果。
正如其他人已经注意到您在代码中有一些进一步的算术错误,但即使纠正它们也不会产生正确分布的随机值。
您不应该使用Math.random()
执行此任务 - 而是使用Random.nextInt(n)
,它旨在正确返回所需范围内的统一值。
正确处理随机数据源非常棘手。根据经验,如果您发现自己在随机值上进行算术运算,那么您可能会做错事(例如生成非均匀结果),您应该寻找一种现有函数来提供您正在寻找的随机性类型相反。
答案 1 :(得分:2)
看起来size
已初始化为orderedDeck.size() - 1
。
由于(int) (Math.random() * size)
会返回[0, size)
范围内的值,即size
本身从未包含,
最后一个元素永远不会被选中。
您可以使用size + 1
作为上限而不是size
来修复:
while (size >= 0) {
int random = (int) (Math.random() * (size + 1));
shuffledDeck.add(orderedDeck.get(random));
orderedDeck.remove(random);
size--;
}
但是,请不要使用此技术来创建混洗列表。
目前的做法非常低效, 因为从列表中间删除元素效率很低。 使用Fisher-Yates shuffle实现高效排序会更好,更容易。 例如:
Random random = new Random();
for (int i = list.size() - 1; i > 0; i--) {
int j = random.nextInt(i);
int tmp = list.get(i);
list.set(i, list.get(j));
list.set(j, tmp);
}
答案 2 :(得分:0)
int size = this.getDeckSize()-1; //51 (52 cards, from 0 to 51)
问题是你从返回的值中减去1。 getDeckSize()
表示你的牌组中有正确数量的元素(52张牌)。你不应该减去1。