魔术介绍
魔术《守岁共此时》是 2024 2024 2024 年春节联欢晚会上刘谦表演的魔术。刘谦共表演了两个魔术,其中的第二个魔术为观众共同参与的魔术。
第二个魔术的操作内容如下。
- 任取 4 4 4 张牌,打乱顺序。
- 将 4 4 4 张牌撕开,变成 8 8 8 个小张,按同一个方向放置。放置之后得到 8 8 8 个小张组成的牌叠,上面 4 4 4 张为 4 4 4 张牌的上半部分,下面 4 4 4 张为 4 4 4 张牌的下半部分,且上面 4 4 4 张与下面 4 4 4 张的相对顺序相同。
- 根据每个人的姓名字数 x x x,每次将牌叠最上面的牌放到最下面,重复 x x x 次该操作。
- 拿起牌叠最上面的 3 3 3 张牌,插进牌叠的中间任意位置。
- 拿起牌叠最上面的 1 1 1 张牌,藏在其他地方。
- 拿起牌叠最上面的 1 1 1 到 3 3 3 张牌,插进牌叠的中间任意位置。具体数量为:南方人拿 1 1 1 张,北方人拿 2 2 2 张,不确定者拿 3 3 3 张。
- 拿起牌叠最上面的 1 1 1 到 2 2 2 张牌,丢弃。具体数量为:男生拿 1 1 1 张,女生拿 2 2 2 张。
- 每次将牌叠最上面的牌放到最下面,重复 7 7 7 次该操作。
- 每次将牌叠最上面的牌放到最下面,然后把牌叠最上面的牌丢弃,直到剩下 1 1 1 张牌。由于第 7 7 7 步男生丢弃的牌比女生少 1 1 1 张,因此第 9 9 9 步男生需要多执行 1 1 1 次操作。
- 剩余的 1 1 1 张牌与第 5 5 5 步藏起的牌匹配。
魔术分析
将 4 4 4 张牌分别记为 A \text{A} A、 B \text{B} B、 C \text{C} C、 D \text{D} D,撕开后 A \text{A} A、 B \text{B} B、 C \text{C} C、 D \text{D} D 各有 2 2 2 张。经过前 2 2 2 步操作之后,牌叠顺序是 ABCDABCD \text{ABCDABCD} ABCDABCD,满足同一张牌的 2 2 2 个半张在牌叠中的距离为 4 4 4。本文中,牌叠顺序都是从上到下的顺序。
第 3 3 3 步操作后的牌叠顺序取决于 x x x 除以 4 4 4 的余数,具体如下。
-
当 x x x 除以 4 4 4 余 0 0 0 时,第 3 3 3 步操作后的牌叠顺序是 ABCDABCD \text{ABCDABCD} ABCDABCD。
-
当 x x x 除以 4 4 4 余 1 1 1 时,第 3 3 3 步操作后的牌叠顺序是 BCDABCDA \text{BCDABCDA} BCDABCDA。
-
当 x x x 除以 4 4 4 余 2 2 2 时,第 3 3 3 步操作后的牌叠顺序是 CDABCDAB \text{CDABCDAB} CDABCDAB。
-
当 x x x 除以 4 4 4 余 3 3 3 时,第 3 3 3 步操作后的牌叠顺序是 DABCDABC \text{DABCDABC} DABCDABC。
虽然可能有 4 4 4 种不同的牌叠顺序,但是都满足同一张牌的 2 2 2 个半张在牌叠中的距离为 4 4 4。以下考虑第 3 3 3 步操作后的牌叠顺序为 ABCDABCD \text{ABCDABCD} ABCDABCD 的情况,对于其他牌叠顺序的情况也适用。
第 4 4 4 步操作之后,牌叠最上面的牌与最下面的牌都是 D \text{D} D,中间的牌的顺序是任意的。
第 5 5 5 步操作将牌叠最上面的牌藏在其他地方,藏起的牌是 D \text{D} D。第 5 5 5 步操作之后,牌叠还有 7 7 7 张牌。
第 6 6 6 步操作会改变牌叠的上面 6 6 6 张牌的顺序,牌叠最下面的牌仍是 D \text{D} D。第 6 6 6 步操作之后,将牌叠顺序记为 EFGHIJD \text{EFGHIJD} EFGHIJD。
第 7 7 7 步操作将牌叠最上面的 1 1 1 到 2 2 2 张牌丢弃,男生丢弃 1 1 1 张牌,女生丢弃 2 2 2 张牌。第 7 7 7 步操作之后,男生的牌叠还有 6 6 6 张牌,牌叠顺序是 FGHIJD \text{FGHIJD} FGHIJD,女生的牌叠还有 5 5 5 张牌,牌叠顺序是 GHIJD \text{GHIJD} GHIJD。
第 8 8 8 步操作将牌叠最上面的 7 7 7 张牌放到最下面。第 8 8 8 步操作之后,男生的牌叠顺序是 GHIJDF \text{GHIJDF} GHIJDF,女生的牌叠顺序是 IJDGH \text{IJDGH} IJDGH。
第 9 9 9 步操作时,男生需要丢弃 5 5 5 张牌,女生需要丢弃 4 4 4 张牌。男生和女生的每次操作之后的牌叠顺序变化如下。
-
男生: GHIJDF → HIJDFG → IJDFG → JDFGI → DFGI → FGID → GID → IDG → DG → GD → D \text{GHIJDF} \rightarrow \text{HIJDFG} \rightarrow \text{IJDFG} \rightarrow \text{JDFGI} \rightarrow \text{DFGI} \rightarrow \text{FGID} \rightarrow \text{GID} \rightarrow \text{IDG} \rightarrow \text{DG} \rightarrow \text{GD} \rightarrow \text{D} GHIJDF→HIJDFG→IJDFG→JDFGI→DFGI→FGID→GID→IDG→DG→GD→D。
-
女生: IJDGH → JDGHI → DGHI → GHID → HID → IDH → DH → HD → D \text{IJDGH} \rightarrow \text{JDGHI} \rightarrow \text{DGHI} \rightarrow \text{GHID} \rightarrow \text{HID} \rightarrow \text{IDH} \rightarrow \text{DH} \rightarrow \text{HD} \rightarrow \text{D} IJDGH→JDGHI→DGHI→GHID→HID→IDH→DH→HD→D。
第 9 9 9 步操作之后,剩余的 1 1 1 张牌是 D \text{D} D,与第 5 5 5 步藏起的牌匹配。
整个魔术过程中,关键的操作有第 4 4 4 步操作、第 5 5 5 步操作、第 7 7 7 步操作、第 8 8 8 步操作和第 9 9 9 步操作,关键操作决定了待匹配的牌 D \text{D} D 的位置与剩余牌的数量。
推广到约瑟夫问题
约瑟夫问题与求解
魔术的第 9 9 9 步操作可以推广到一般形式:有 n n n 张牌围成一个圆圈,按顺时针顺序的编号依次是 1 1 1 到 n n n,从编号 1 1 1 的牌开始,每次向顺时针方向数 k k k 张牌并将第 k k k 张牌丢弃,直到最后只剩余 1 1 1 张牌,需要计算剩余的牌的编号。该问题为经典的约瑟夫问题。
约瑟夫问题的解法有多种。最简单的解法是模拟,时间复杂度是 O ( n k ) O(nk) O(nk),空间复杂度是 O ( n ) O(n) O(n)。更优的解法是数学解法,时间复杂度是 O ( n ) O(n) O(n),数学解法可以使用递归或迭代实现,递归实现的空间复杂度是 O ( n ) O(n) O(n),迭代实现的空间复杂度是 O ( 1 ) O(1) O(1)。
用 f ( n , k ) f(n, k) f(n,k) 表示约瑟夫问题的解。可以使用递归的方式计算 f ( n , k ) f(n, k) f(n,k)。
递归的基准情形是 n = 1 n = 1 n=1,此时圆圈中只有 1 1 1 张编号是 1 1 1 的牌,因此 f ( 1 , k ) = 1 f(1, k) = 1 f(1,k)=1。
当 n > 1 n > 1 n>1 时,记 prevRemain = f ( n − 1 , k ) \textit{prevRemain} = f(n - 1, k) prevRemain=f(n−1,k),则 prevRemain \textit{prevRemain} prevRemain 为 n − 1 n - 1 n−1 张牌中剩余的牌。如果 n n n 张牌的起始牌的编号是 1 1 1,则首张被丢弃的牌的编号是 k k k,剩余 n − 1 n - 1 n−1 张牌时的起始牌的编号是 1 + k 1 + k 1+k。因此, n n n 张牌时剩余的牌的编号是 prevRemain \textit{prevRemain} prevRemain 按顺时针方向数 k k k 位。
由于牌的编号范围是 [ 1 , n ] [1, n] [1,n],因此 f ( n , k ) = ( prevRemain + k − 1 ) m o d n + 1 f(n, k) = (\textit{prevRemain} + k - 1) \bmod n + 1 f(n,k)=(prevRemain+k−1)modn+1。
迭代实现根据递归实现得到。用 winner \textit{winner} winner 表示剩余的牌的编号,初始时 winner = 1 \textit{winner} = 1 winner=1。对于 2 ≤ i ≤ n 2 \le i \le n 2≤i≤n,从小到大遍历每个 i i i,并将 winner \textit{winner} winner 的值更新为 ( winner + k − 1 ) m o d i + 1 (\textit{winner} + k - 1) \bmod i + 1 (winner+k−1)modi+1。遍历结束时, winner \textit{winner} winner 即为 n n n 张牌时剩余的牌的编号。
代码实现
下面的代码为数学解法的递归实现。
class Solution {
public int findRemainCard(int n, int k) {
if (n == 1) {
return 1;
}
int prevRemain = findRemainCard(n - 1, k);
return (prevRemain + k - 1) % n + 1;
}
}
下面的代码为数学解法的迭代实现。
class Solution {
public int findRemainCard(int n, int k) {
int remain = 1;
for (int i = 2; i <= n; i++) {
remain = (remain + k - 1) % i + 1;
}
return remain;
}
}