【发布时间】:2013-04-28 00:25:31
【问题描述】:
当我需要在 Java/Android 中洗牌时,我当然会使用Collections.shuffle(List<?> list)。我曾经这样做过,结果似乎可以接受。但他们不是。
如this paper 中所述,共有 52 个! 52张扑克牌的可能独特的洗牌。这相当于大约 2^226。
但Collections.shuffle(List<?> list) 默认使用new Random(),它使用48-bit seed,因此只能创建 2^48 个唯一的随机播放 - 这仅占所有可能随机播放的 3.49*10^(-52)%!
那么我该如何正确洗牌呢?
我已经开始使用SecureRandom,但最后就够了吗?
List<Card> cards = new ArrayList<Card>();
...
SecureRandom secureRandom;
try {
secureRandom = SecureRandom.getInstance("SHA1PRNG");
}
catch (NoSuchAlgorithmException e) {
secureRandom = new SecureRandom();
}
secureRandom.nextBytes(new byte[20]); // force SecureRandom to seed itself
Collections.shuffle(cards, secureRandom);
【问题讨论】:
-
来自论文:首先要意识到的是,一种算法能够产生 52 个中的每一个!洗牌并不是真正需要的。
-
@Philipp Sander:重复洗牌不会增加随机性,是吗?
-
blog.uncommons.org/2008/04/10/… 这可能会有所帮助,我还根据文章更新了我对非完全废话的回答。
-
@MarcoW.Mersenne Twister 产生 32 位或 64 位结果,但它基于更大的内部状态 - 它的循环长度为 2^19937-1。
-
@MarcoW。我相信确实如此。在任何单种子生成器中,例如 LCG 的,一旦您第二次看到给定值,整个序列将被相同地重复。当较大的状态折叠/投影到较小的输出状态时,看到相同值的重复并不意味着您将获得相同序列的重复。至于播种,那只是选择进入大周期的切入点。 64 位种子意味着您选择大约 10^18 个入口点之一进入长度为 2^19937-1 的循环。
标签: java android random shuffle playing-cards