问题背景
完美洗牌:一副 52 张的排序好的扑克牌,从中间分为两半,每部分各 26 张。假设每次都分为左右两部分,然后将
右部分的牌和左部分的牌按顺序交错穿插,每张左部分牌后面加入一张右部分的,依序加入所有右部分牌。
完成一次穿插后得到新的牌堆。
一副排序好的扑克牌(52张),在完成第 8 次完美洗牌后,将得到和初始顺序一致的牌堆。
解答目标
求出一个通用算法:
给出任意数量的扑克牌,求经过多少次完美洗牌后恢复原序
数据打样分析
将扑克牌按顺序编号为 0 ~ 51,8 次完美洗牌的中间过程如下
1: [0, 26, 1, 27, 2, 28, 3, 29, 4, 30, 5, 31, 6, 32, 7, 33, 8, 34, 9, 35, 10, 36, 11, 37, 12, 38, 13, 39, 14, 40, 15, 41, 16, 42, 17, 43, 18, 44, 19, 45, 20, 46, 21, 47, 22, 48, 23, 49, 24, 50, 25, 51]
2: [0, 13, 26, 39, 1, 14, 27, 40, 2, 15, 28, 41, 3, 16, 29, 42, 4, 17, 30, 43, 5, 18, 31, 44, 6, 19, 32, 45, 7, 20, 33, 46, 8, 21, 34, 47, 9, 22, 35, 48, 10, 23, 36, 49, 11, 24, 37, 50, 12, 25, 38, 51]
3: [0, 32, 13, 45, 26, 7, 39, 20, 1, 33, 14, 46, 27, 8, 40, 21, 2, 34, 15, 47, 28, 9, 41, 22, 3, 35, 16, 48, 29, 10, 42, 23, 4, 36, 17, 49, 30, 11, 43, 24, 5, 37, 18, 50, 31, 12, 44, 25, 6, 38, 19, 51]
4: [0, 16, 32, 48, 13, 29, 45, 10, 26, 42, 7, 23, 39, 4, 20, 36, 1, 17, 33, 49, 14, 30, 46, 11, 27, 43, 8, 24, 40, 5, 21, 37, 2, 18, 34, 50, 15, 31, 47, 12, 28, 44, 9, 25, 41, 6, 22, 38, 3, 19, 35, 51]
5: [0, 8, 16, 24, 32, 40, 48, 5, 13, 21, 29, 37, 45, 2, 10, 18, 26, 34, 42, 50, 7, 15, 23, 31, 39, 47, 4, 12, 20, 28, 36, 44, 1, 9, 17, 25, 33, 41, 49, 6, 14, 22, 30, 38, 46, 3, 11, 19, 27, 35, 43, 51]
6: [0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51]
7: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51]
8: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
第 8 次开始倒着分析,初始每个数据相差 1,
第 7 次各个数据的间隔变为 2 的 1 次方;
第 6 次各个数据的间隔变为 2 的 2 次方;
第 5 次各个数据的间隔变为 2 的 3 次方;
第 4 次各个数据的间隔变为 2 的 4 次方;
第 3 次各个数据的间隔变为 2 的 5 次方(32) mod 51 = 32;
第 2 次各个数据的间隔变为 2 的 6 次方(64) mod 51 = 13;
第 1 次各个数据的间隔变为 2 的 7 次方(128) mod 51 = 26;
第 0 次各个数据的间隔变为 2 的 8 次方(256) mod 51 = 1; (原始顺序)
可见反过来分析后,经过 8 次完美洗牌后,扑克牌又变回了原始顺序,
总结上面的算法,得到模数公式如下,设需要经过 n + 1 次完美洗牌后,一副 m 张扑克牌的排序变为原始排序, m 为偶数。
2
n
≡
m
2
(
m
o
d
m
−
1
)
2^n \equiv \frac{m}{2} \pmod{m-1}
2n≡2m(modm−1)
转为数学计算公式
(
m
−
1
)
∣
2
n
−
m
2
,
(
m
>
=
2
)
2
n
−
m
2
=
(
m
−
1
)
k
,
k
∈
z
,
n
>
=
log
2
m
2
(
n
>
=
log
2
m
−
1
)
(m - 1) | 2^n - \frac{m}{2},(m >= 2) \\ 2^n - \frac{m}{2} = (m - 1)k, k \in z, n >= \log_{2}{\frac{m}{2}} \\ (n >= \log_{2}{m} - 1)
(m−1)∣2n−2m,(m>=2)2n−2m=(m−1)k,k∈z,n>=log22m(n>=log2m−1)
编程计算
import math
def test():
# 51 | 2 ** n - 26
m = 52
b = m // 2
c = m - 1
n = math.ceil(math.log2(m) - 1)
a = 1 << n
while True:
if (a - b) % c == 0:
print("success", n)
break
n += 1
a <<= 1
## res:
## success 7
test()
可以求得在 m 等于 52 时,n 等于 7, 即 52 张扑克牌经过 n + 1 = 8 次完美洗牌后会恢复原始排序。
拓展学习
上面的分析是基于 0 ~ 51 的编号,也就是和原始的扑克牌花色点数没有关系,任意 52 张扑克牌做了 8 次完美洗牌后,都会恢复到原始的序位。
可以将扑克牌的数量增加 8 张,相当于每种花色多了 2 张牌,这时扑克牌的总数是 60 张,套入上面的算法可以求得经过 58 次完美洗牌后,60 张牌会和未洗牌的顺序一样。
还可以深入研究的点:
- 什么情况下会无解
- 在小于 52 张的时候,什么情况无解
- 是否存在从某个数 x x x 开始,当扑克牌的数量 n > = x n >= x n>=x 时, 再也找不到解