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

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

1.魔术模拟

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

约瑟夫环问题的公式为:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f ( n , m ) = { 0 n=1 [ f ( n − 1 , m ) + m ] % n n>1 f(n,m)=\begin{cases} 0& \text{n=1}\\ [f(n-1,m)+m]\%n & \text{n>1} \end{cases} </math>f(n,m)={0[f(n−1,m)+m]%nn=1n>1

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

根据公式可知: <math xmlns="http://www.w3.org/1998/Math/MathML"> f ( 6 , 2 ) = 4 f(6,2)=4 </math>f(6,2)=4; <math xmlns="http://www.w3.org/1998/Math/MathML"> f ( 5 , 2 ) = 2 f(5,2)=2 </math>f(5,2)=2

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

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

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

1.问题描述

假设有 <math xmlns="http://www.w3.org/1998/Math/MathML"> n n </math>n 个人围成一个圈,根据某一顺序为每一个人设置一个编号 <math xmlns="http://www.w3.org/1998/Math/MathML"> 0 、 1 、 2 、 ... 、 n − 1 0、1、2、\dots、n-1 </math>0、1、2、...、n−1。从编号 0 开始报数,报到 <math xmlns="http://www.w3.org/1998/Math/MathML"> m m </math>m 的人出圈;然后从出圈人的下一个人重新开始报数。重复该操作,直到只剩下一个人,这个人就是赢家。

2.示例说明

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

3.数学推导

定义:

  • 在序列 <math xmlns="http://www.w3.org/1998/Math/MathML"> 0 、 1 、 2 、 ... 、 n − 1 0、1、2、\dots、n-1 </math>0、1、2、...、n−1 执行约瑟夫环操作,最后剩下的编号为 <math xmlns="http://www.w3.org/1998/Math/MathML"> f ( n , m ) f(n,m) </math>f(n,m)
  • 执行完第一次约瑟夫环操作后,出圈的编号为 <math xmlns="http://www.w3.org/1998/Math/MathML"> k k </math>k,则下一次操作的序列为 <math xmlns="http://www.w3.org/1998/Math/MathML"> k + 1 、 k + 2 、 ... 、 n − 1 、 0 、 1 、 ... 、 k − 1 k+1、k+2、\dots、n-1、0、1、\dots、k-1 </math>k+1、k+2、...、n−1、0、1、...、k−1,在该序列上的约瑟夫环操作,最后剩下的编号为 <math xmlns="http://www.w3.org/1998/Math/MathML"> g ( n − 1 , m ) g(n-1,m) </math>g(n−1,m)
  • 从以上的定义可知, <math xmlns="http://www.w3.org/1998/Math/MathML"> f ( n , m ) = g ( n − 1 , m ) f(n,m)=g(n-1,m) </math>f(n,m)=g(n−1,m)

重新映射:

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

原编号 新编号
<math xmlns="http://www.w3.org/1998/Math/MathML"> k + 1 k+1 </math>k+1 <math xmlns="http://www.w3.org/1998/Math/MathML"> 0 0 </math>0
<math xmlns="http://www.w3.org/1998/Math/MathML"> k + 2 k+2 </math>k+2 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1
<math xmlns="http://www.w3.org/1998/Math/MathML"> ... \dots </math>... <math xmlns="http://www.w3.org/1998/Math/MathML"> ... \dots </math>...
<math xmlns="http://www.w3.org/1998/Math/MathML"> n − 1 n-1 </math>n−1 <math xmlns="http://www.w3.org/1998/Math/MathML"> n − k − 2 n-k-2 </math>n−k−2
<math xmlns="http://www.w3.org/1998/Math/MathML"> 0 0 </math>0 <math xmlns="http://www.w3.org/1998/Math/MathML"> n − k − 1 n-k-1 </math>n−k−1
<math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1 <math xmlns="http://www.w3.org/1998/Math/MathML"> n − k n-k </math>n−k
<math xmlns="http://www.w3.org/1998/Math/MathML"> ... \dots </math>... <math xmlns="http://www.w3.org/1998/Math/MathML"> ... \dots </math>...
<math xmlns="http://www.w3.org/1998/Math/MathML"> k − 1 k-1 </math>k−1 <math xmlns="http://www.w3.org/1998/Math/MathML"> n − 2 n-2 </math>n−2

根据以上的映射关系,可以总结得到以下的公式:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> h ( x ) = { x − k − 1 k+1 <= x <= n-1 x + n − k − 1 0 <= x <=k-1 h(x)=\begin{cases} x-k-1& \text{k+1 <= x <= n-1 }\\ x+n-k-1 & \text{0 <= x <=k-1} \end{cases} </math>h(x)={x−k−1x+n−k−1k+1 <= x <= n-1 0 <= x <=k-1

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∵ k + 1 ≤ x ≤ n − 1 → 1 ≤ x − k ≤ n − k − 1 → 0 ≤ x − k − 1 ≤ n − k − 2 \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 </math>∵k+1≤x≤n−1 → 1≤x−k≤n−k−1 → 0≤x−k−1≤n−k−2

<math xmlns="http://www.w3.org/1998/Math/MathML"> n ≤ x + n − k − 1 ≤ n + n − k − 2 \ \ \ n \le x+n-k-1 \le n+n-k-2 </math> n≤x+n−k−1≤n+n−k−2

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ n % n = 0 ≤ ( x + n − k − 1 ) % n ≤ ( 2 ∗ n − k − 2 ) % n = n − k − 2 \therefore n\%n=0 \le (x+n-k-1)\%n \le (2*n-k-2)\%n=n-k-2 </math>∴n%n=0≤(x+n−k−1)%n≤(2∗n−k−2)%n=n−k−2

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ 式子 ( x + n − k − 1 ) % n ,当 k + 1 ≤ x ≤ n − 1 时, 0 ≤ h ( x ) ≤ n − k − 2 \therefore 式子 (x+n-k-1)\%n,当\ k+1 \le x \le n-1\ 时,0 \le h(x) \le n-k-2 </math>∴式子(x+n−k−1)%n,当 k+1≤x≤n−1 时,0≤h(x)≤n−k−2

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∵ 0 ≤ x ≤ k − 1 → n ≤ x + n ≤ n + k − 1 → n − k ≤ x + n − k ≤ n − 1 \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 </math>∵0≤x≤k−1 → n≤x+n≤n+k−1 → n−k≤x+n−k≤n−1

<math xmlns="http://www.w3.org/1998/Math/MathML"> n − k − 1 ≤ x + n − k − 1 ≤ n − 2 \ \ \ n-k-1 \le x+n-k-1 \le n-2 </math> n−k−1≤x+n−k−1≤n−2

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ ( n − k − 1 ) % n = n − k − 1 ≤ ( x + n − k − 1 ) % n ≤ ( n − 2 ) % n = n − 2 \therefore (n-k-1)\%n=n-k-1 \le (x+n-k-1)\%n \le (n-2)\%n=n-2 </math>∴(n−k−1)%n=n−k−1≤(x+n−k−1)%n≤(n−2)%n=n−2

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ 式子 ( x + n − k − 1 ) % n ,当 0 ≤ x ≤ k − 1 时, n − k − 1 ≤ h ( x ) ≤ n − 2 \therefore 式子 (x+n-k-1)\%n,当\ 0 \le x \le k-1\ 时,n-k-1 \le h(x) \le n-2 </math>∴式子(x+n−k−1)%n,当 0≤x≤k−1 时,n−k−1≤h(x)≤n−2

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ 可将公式统一为 h ( x ) = ( x + n − k − 1 ) % n \therefore 可将公式统一为\ \ h(x)=(x+n-k-1)\%n </math>∴可将公式统一为 h(x)=(x+n−k−1)%n

为了消除求余号 <math xmlns="http://www.w3.org/1998/Math/MathML"> % \% </math>% ,可以引入正整数 <math xmlns="http://www.w3.org/1998/Math/MathML"> T T </math>T,以达到求余的效果;公式如下所示:

<math xmlns="http://www.w3.org/1998/Math/MathML"> h ( x ) = ( x + n − k − 1 ) % n = ( x + n − k − 1 ) + ( T − 1 ) n = x − k − 1 + T n h(x)=(x+n-k-1)\%n=(x+n-k-1)+(T-1)n=x-k-1+Tn </math>h(x)=(x+n−k−1)%n=(x+n−k−1)+(T−1)n=x−k−1+Tn

推导:

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∵ f ( n − 1 , m ) \because f(n-1,m) </math>∵f(n−1,m) 是在重新映射后的序列上的约瑟夫环操作结果

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ f ( n − 1 , m ) = h ( g ( n − 1 , m ) ) \therefore f(n-1,m)=h(g(n-1,m)) </math>∴f(n−1,m)=h(g(n−1,m))

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ g ( n − 1 , m ) = h − 1 ( f ( n − 1 , m ) ) \therefore g(n-1,m)=h^{-1}(f(n-1,m)) </math>∴g(n−1,m)=h−1(f(n−1,m))

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∵ x = h ( x ) + k + 1 − T n \because x=h(x)+k+1-Tn </math>∵x=h(x)+k+1−Tn

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ h − 1 ( x ) = x + k + 1 − T n = ( x + k + 1 ) % n \therefore h^{-1}(x)=x+k+1-Tn=(x+k+1)\%n </math>∴h−1(x)=x+k+1−Tn=(x+k+1)%n

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ g ( n − 1 , m ) = h − 1 ( f ( n − 1 , m ) ) = [ f ( n − 1 , m ) + k + 1 ] % n \therefore g(n-1,m)=h^{-1}(f(n-1,m))=[f(n-1,m)+k+1]\%n </math>∴g(n−1,m)=h−1(f(n−1,m))=[f(n−1,m)+k+1]%n

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∵ k \because k </math>∵k 是第一次约瑟夫环操作的结果,可知 <math xmlns="http://www.w3.org/1998/Math/MathML"> k = ( m − 1 ) % n k=(m-1)\%n </math>k=(m−1)%n

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ g ( n − 1 , m ) = [ f ( n − 1 , m ) + ( m − 1 ) % n + 1 ] % n = [ f ( n − 1 , m ) + m ] % n \therefore g(n-1,m)=[f(n-1,m)+(m-1)\%n+1]\%n=[f(n-1,m)+m]\%n </math>∴g(n−1,m)=[f(n−1,m)+(m−1)%n+1]%n=[f(n−1,m)+m]%n

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∵ f ( n , m ) = g ( n − 1 , m ) \because f(n,m)=g(n-1,m) </math>∵f(n,m)=g(n−1,m)

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∴ f ( n , m ) = [ f ( n − 1 , m ) + m ] % n \therefore f(n,m)=[f(n-1,m)+m]\%n </math>∴f(n,m)=[f(n−1,m)+m]%n

代码:

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

3.参考文献

相关推荐
luthane25 分钟前
python 实现average mean平均数算法
开发语言·python·算法
静心问道29 分钟前
WGAN算法
深度学习·算法·机器学习
杰九1 小时前
【算法题】46. 全排列-力扣(LeetCode)
算法·leetcode·深度优先·剪枝
manba_1 小时前
leetcode-560. 和为 K 的子数组
数据结构·算法·leetcode
liuyang-neu1 小时前
力扣 11.盛最多水的容器
算法·leetcode·职场和发展
忍界英雄1 小时前
LeetCode:2398. 预算内的最多机器人数目 双指针+单调队列,时间复杂度O(n)
算法·leetcode·机器人
Kenneth風车1 小时前
【机器学习(五)】分类和回归任务-AdaBoost算法-Sentosa_DSML社区版
人工智能·算法·低代码·机器学习·数据分析
C7211BA1 小时前
使用knn算法对iris数据集进行分类
算法·分类·数据挖掘
Tisfy2 小时前
LeetCode 2398.预算内的最多机器人数目:滑动窗口+单调队列——思路清晰的一篇题解
算法·leetcode·机器人·题解·滑动窗口
程序猿练习生2 小时前
C++速通LeetCode简单第18题-杨辉三角(全网唯一递归法)
c++·算法·leetcode