2024春晚魔术原理—约瑟夫环问题

2,417 阅读4分钟

首先祝大家新年快乐,龙年大吉!相信大家都对昨晚春晚的魔术记忆深刻(即使没看过,也一定看到了尼格买提穿帮的新闻)。接下来就来模拟一下昨晚刘谦的魔术,并扒扒其原理。

1.魔术模拟

Step 1:假设有四张牌,编号为 1、2、3、4

卡牌.png

Step 2:四张卡牌对折撕开,并重叠

卡牌2.png

Step 3:根据自己名字的字数下放牌;例如名字字数为 2,则序列如下所示

卡牌3.png

Notice:这是无效操作,并不会对最终效果产生影响。

Step 4:拿起前三张牌,插进剩下牌的中间

卡牌4.png

Notice:这一步操作,要求插进中间的卡牌数必须为 3,因为两张相同牌的中间隔了 3 张卡牌;经过这样的操作后,能够保证头和尾是相同卡牌;其次插进的位置只要不是头和尾,不会对结果造成影响

Step 5:隐藏第一张卡牌,此时隐藏的卡牌是 2

卡牌5.png

Step 6:南方朋友拿起前一张卡牌;北方朋友拿起前两张卡牌;自己不知道是南方和北方的朋友拿起前三张卡牌。将这些卡牌插入剩下卡牌的中间。示例拿起一张,结果如下所示

卡牌6.png

Notice:这一步操作是无效操作;无论拿几张牌,以及插入在哪个位置,只要保证卡牌 2 在最后即可

Step 7:男生拿起前一张卡牌;女生拿起前两张卡牌;将这些卡牌扔掉。两种情况如下所示

卡牌7.png

Notice:这一步的操作是保证卡牌总数为 6 或 5

Step 8:大喊 “见证奇迹的时刻”,每喊一个字将一张卡牌下方,总共下放 7 次

卡牌8.png

Notice:这一步是关键操作,其主要目的是为了将卡牌 2 放置到最后一步的约瑟夫环问题的答案位置

Step 9:第一张卡牌将其下放,第二张卡牌将其丢掉;该步骤操作四次

卡牌9.png

Notice:这一步操作的本质是约瑟夫环问题;其将这一序列排成一个环,为每个卡牌编号(从0开始,如 Step 8 所示),从 0 开始报数,报到 2 的卡牌移除;然后从移除卡牌的下一个卡牌开始重新报数,最后留下的卡牌就是与隐藏卡牌配对的卡牌。

约瑟夫环问题的公式为:

f(n,m)={0n=1[f(n1,m)+m]%nn>1f(n,m)=\begin{cases} 0& \text{n=1}\\ [f(n-1,m)+m]\%n & \text{n>1} \end{cases}

其中 n 代表卡牌数,m = 2。

根据公式可知:f(6,2)=4f(6,2)=4f(5,2)=2f(5,2)=2

其结果正好是 Step 8 中卡牌 2 的位置。

代码:

通过 Java 语言实现了模拟程序,其代码已上传至 Github,链接在下方。执行结果如下图所示:

模拟程序运行结果.png

2024 SpringFestivalMagic (github.com)

根据以上的模拟过程是不是已经清楚这个魔术的原理了呢~接下来讨论一下约瑟夫环问题的推导过程。

2.约瑟夫环问题数学推导过程

1.问题描述

假设有 nn 个人围成一个圈,根据某一顺序为每一个人设置一个编号 012n10、1、2、\dots、n-1。从编号 0 开始报数,报到 mm 的人出圈;然后从出圈人的下一个人重新开始报数。重复该操作,直到只剩下一个人,这个人就是赢家。

2.示例说明

假设有 6 个人,报到 2 的人出圈。其执行过程如下所示:

约瑟夫环问题流程.png

3.数学推导

定义:

  • 在序列 012n10、1、2、\dots、n-1 执行约瑟夫环操作,最后剩下的编号为 f(n,m)f(n,m)
  • 执行完第一次约瑟夫环操作后,出圈的编号为 kk,则下一次操作的序列为 k+1k+2n101k1k+1、k+2、\dots、n-1、0、1、\dots、k-1,在该序列上的约瑟夫环操作,最后剩下的编号为 g(n1,m)g(n-1,m)
  • 从以上的定义可知,f(n,m)=g(n1,m)f(n,m)=g(n-1,m)

重新映射:

将执行完第一次约瑟夫环操作后得到的新序列进行重新编号,从 0 开始,则其映射关系如下表所示:

原编号新编号
k+1k+100
k+2k+211
\dots\dots
n1n-1nk2n-k-2
00nk1n-k-1
11nkn-k
\dots\dots
k1k-1n2n-2

根据以上的映射关系,可以总结得到以下的公式:

h(x)={xk1k+1 <= x <= n-1 x+nk10 <= x <=k-1h(x)=\begin{cases} x-k-1& \text{k+1 <= x <= n-1 }\\ x+n-k-1 & \text{0 <= x <=k-1} \end{cases}

k+1xn1    1xknk1    0xk1nk2\because k+1 \le x \le n-1 \ \ \rightarrow \ \ 1 \le x-k \le n-k-1 \ \ \rightarrow \ \ 0 \le x-k-1\le n-k-2

   nx+nk1n+nk2\ \ \ n \le x+n-k-1 \le n+n-k-2

n%n=0(x+nk1)%n(2nk2)%n=nk2\therefore n\%n=0 \le (x+n-k-1)\%n \le (2*n-k-2)\%n=n-k-2

式子(x+nk1)%n,当 k+1xn1 时,0h(x)nk2\therefore 式子 (x+n-k-1)\%n,当\ k+1 \le x \le n-1\ 时,0 \le h(x) \le n-k-2

0xk1    nx+nn+k1    nkx+nkn1\because 0 \le x \le k-1 \ \ \rightarrow \ \ n \le x+n \le n+k-1 \ \ \rightarrow \ \ n-k \le x+n-k \le n-1

   nk1x+nk1n2\ \ \ n-k-1 \le x+n-k-1 \le n-2

(nk1)%n=nk1(x+nk1)%n(n2)%n=n2\therefore (n-k-1)\%n=n-k-1 \le (x+n-k-1)\%n \le (n-2)\%n=n-2

式子(x+nk1)%n,当 0xk1 时,nk1h(x)n2\therefore 式子 (x+n-k-1)\%n,当\ 0 \le x \le k-1\ 时,n-k-1 \le h(x) \le n-2

可将公式统一为  h(x)=(x+nk1)%n\therefore 可将公式统一为\ \ h(x)=(x+n-k-1)\%n

为了消除求余号 %\% ,可以引入正整数 TT,以达到求余的效果;公式如下所示:

h(x)=(x+nk1)%n=(x+nk1)+(T1)n=xk1+Tnh(x)=(x+n-k-1)\%n=(x+n-k-1)+(T-1)n=x-k-1+Tn

推导:

f(n1,m)\because f(n-1,m) 是在重新映射后的序列上的约瑟夫环操作结果

f(n1,m)=h(g(n1,m))\therefore f(n-1,m)=h(g(n-1,m))

g(n1,m)=h1(f(n1,m))\therefore g(n-1,m)=h^{-1}(f(n-1,m))

x=h(x)+k+1Tn\because x=h(x)+k+1-Tn

h1(x)=x+k+1Tn=(x+k+1)%n\therefore h^{-1}(x)=x+k+1-Tn=(x+k+1)\%n

g(n1,m)=h1(f(n1,m))=[f(n1,m)+k+1]%n\therefore g(n-1,m)=h^{-1}(f(n-1,m))=[f(n-1,m)+k+1]\%n

k\because k 是第一次约瑟夫环操作的结果,可知 k=(m1)%nk=(m-1)\%n

g(n1,m)=[f(n1,m)+(m1)%n+1]%n=[f(n1,m)+m]%n\therefore g(n-1,m)=[f(n-1,m)+(m-1)\%n+1]\%n=[f(n-1,m)+m]\%n

f(n,m)=g(n1,m)\because f(n,m)=g(n-1,m)

f(n,m)=[f(n1,m)+m]%n\therefore f(n,m)=[f(n-1,m)+m]\%n

代码:

public int findWinner(int n,int m){
    if(n==1) return 0;
    int winner = findWinner(n-1,m);
    return (winner+m)%n;
}

3.参考文献