我知道这篇文章已经快 4 年了,但我是一名密码分析爱好者,并且一直在研究扑克牌密码。因此,我一遍又一遍地回到这篇文章,解释牌组洗牌是随机键控牌组的熵源。最后,我决定通过手动洗牌并估计每次洗牌后的牌组熵来通过 stachyra 来验证答案。
TL;DR,最大化甲板熵:
- 对于仅浅滩洗牌,您需要 11-12 次洗牌。
- 对于先切牌然后洗牌,您只需要 6-7 次切牌和洗牌。
首先,stachyra 提到的用于计算香农熵的所有内容都是正确的。可以这样归结:
- 为一副牌中的 52 张卡片中的每一张数字分配一个唯一值。
- 洗牌。
- 对于 n=0 到 n=51,记录 (n - (n+1) mod 52) mod 52 的每个值
- 计算 0、1、2、...、49、50、51 的出现次数
- 通过将每个记录除以 52 来规范化这些记录
- 对于 i=1 到 i=52,计算 -p_i * log(p_i)/log(2)
- 对值求和
stachyra 做了一个微妙的假设,那就是在计算机程序中实现人类洗牌会带来一些包袱。使用纸质纸牌时,手上的油会转移到纸牌上。在很长一段时间内,由于油的积累,卡片会开始粘在一起,这最终会出现在你的洗牌中。牌组使用得越频繁,两张或多张相邻的牌就越有可能粘在一起,而且发生的频率也越高。
此外,假设俱乐部和红心杰克两者粘在一起。在你洗牌的过程中,它们最终可能会粘在一起,永远不会分开。这可以在计算机程序中进行模仿,但 stachyra 的 R 例程并非如此。
此外,stachyra 有一个操作变量“mixprob”。在没有完全理解这个变量的情况下,它有点像一个黑匣子。您可能会错误地设置它,从而影响结果。所以,我想确保他的直觉是正确的。所以我手动验证了它。
在两种不同的情况下(总共 40 次洗牌),我手动洗牌了 20 次。在第一种情况下,我只是洗牌,保持左右切口接近均匀。在第二种情况下,我故意将牌组从牌组的中间(1/3、2/5、1/4 等)剪开,然后再进行均匀剪裁以进行 riffle shuffle。在第二种情况下,我的直觉是,通过在洗牌前切割牌组,并远离中间,我可以比普通洗牌更快地将扩散引入牌组。
这是结果。首先,直式洗牌:
这里是切牌结合浅滩洗牌:
似乎熵在 stachyra 声称的大约 1/2 时间内最大化。此外,我的直觉是正确的,首先故意将甲板从中间切开,然后再洗牌确实将更多的扩散引入甲板。然而,在大约 5 次洗牌之后,它不再那么重要了。您可以看到,在大约 6-7 次洗牌后,熵最大化,而 10-12 次是我声称的 stachyra。有没有可能 7 次洗牌就足够了,还是我被蒙蔽了?
您可以在 Google 表格中查看我的数据。有可能我记错了一两张扑克牌,所以我不能保证数据 100% 准确。
您的发现也必须经过独立验证,这一点很重要。哈佛大学数学系的 Brad Mann 研究了在牌组中任何一张牌的可预测性完全不可预测(香农熵最大化)之前,将一副牌洗牌需要多少次。他的结果可以在这个 33 页的 PDF中找到。
他的发现有趣的是,他实际上是在独立验证Persi Diaconis 1990 年《纽约时报》的一篇文章,该文章声称 7 次洗牌足以通过 riffle shuffle 彻底混合一副扑克牌。
Brad Mann 浏览了几个不同的洗牌数学模型,包括马尔可夫链,并得出以下结论:
对于 n=52,这大约是 11.7,这意味着,根据这个观点,我们预计平均需要 11 或 12 次洗牌才能随机化一副真正的纸牌。请注意,这大大大于 7。
Brad Mann 只是独立验证了 stachyra 的结果,而不是我的。所以,我仔细查看了我的数据,我发现为什么 7 次随机播放是不够的。首先,对于牌组中的任何卡片,理论上的最大香农熵(以位为单位)为 log(52)/log(2) ~= 5.7 bits。但我的数据从未真正突破超过 5 位。好奇的是,我在 Python 中创建了一个包含 52 个元素的数组,并对该数组进行了洗牌:
>>> import random
>>> r = random.SystemRandom()
>>> d = [x for x in xrange(1,52)]
>>> r.shuffle(d)
>>> print d
[20, 51, 42, 44, 16, 5, 18, 27, 8, 24, 23, 13, 6, 22, 19, 45, 40, 30, 10, 15, 25, 37, 52, 34, 12, 46, 48, 3, 26, 4, 1, 38, 32, 14, 43, 7, 31, 50, 47, 41, 29, 36, 39, 49, 28, 21, 2, 33, 35, 9, 17, 11]
计算其每张卡的熵产生大约 4.8 位。这样做十几次显示类似的结果在 5.2 位和 4.6 位之间变化,平均为 4.8 到 4.9。所以只看我数据的原始熵值是不够的,否则我可以称之为擅长 5 次随机播放。
当我仔细查看我的数据时,我注意到“零桶”的数量。这些存储桶中没有该数字的卡面之间的增量数据。例如,当减去相邻两张牌的值时,在计算完所有 52 个 delta 之后,没有“15”的结果。
我看到它最终在 11-12 次洗牌时解决了 17-18 个“零桶”。果然,我通过 Python 洗牌的牌组平均有 17-18 个“零桶”,最高为 21,最低为 14。为什么 17-18 是结算结果,我还无法解释...... 但是,似乎我想要约 4.8 位熵和 17 个“零桶”。
随着我的股票浅滩洗牌,那是 11-12 洗牌。用我的剪切和洗牌,那是6-7。所以,当谈到游戏时,我会推荐随机播放。这不仅保证了每次洗牌时顶部和底部的牌都混入牌库,而且比 11-12 次洗牌要快得多。我不了解你,但是当我和家人朋友玩纸牌游戏时,他们没有足够的耐心让我进行 12 次洗牌。