刘谦为什么一定要用7字咒语和4张牌?:春晚魔术背后的数学逻辑

因为小尼没对上牌而爆火的春晚魔术《守岁共此时》

  • 是否让你想起刚学编程时做的一道题目?
  • 为什么刘谦一定要用 7 字咒语,4 张牌?
  • 如果换一个咒语,魔术要怎么设计?牌数要变吗?
  • 小尼没对上牌的根本原因是什么?

如果有这些疑问,本文将为你揭晓。

了解一样东西最好的方法就是将它重新做一遍,在了解底层数学逻辑之前,我们先尝试换个咒语,从头设计一个同款魔术。

从头设计一个同款魔术

第一步 编个咒语

编个神秘的咒语,字数务必是2的幂次减1

刘谦的咒语是"见证奇迹的时刻",7个字,是2的三次方减1。

我也编一个咒语 "龙年大吉,快关注技乐书香,好运不断" 总计15个字,为2的四次方减1。

第二步 算牌数

需要的牌数为比咒语字数小的最大的2的幂次

刘谦的咒语7个字,比7小的最大2幂次为4(2的平方),所以刘谦用了4张牌。

我的咒语为15个字,比15小的最大2幂次为8(2的三次方),所以我需要用8张牌。

第三步 关键魔术步骤

以下四步为最为关键魔术步骤:

  • 步骤一:8张牌(上一步算出的牌数)对折撕开,堆到一起,撕开后的牌堆共16(2*8)张(半)牌
  • 步骤二:将牌顶7(=8-1)张牌插入中间经过这一步,堆顶和堆底就是配对的两张牌了
  • 步骤三:将堆顶的牌藏到屁股底下此时堆里还剩15张牌
  • 最后一步:留一张(从堆顶移动到堆底),丢一张,循环往复直到剩下最后一张牌。其实最后一张就是堆底,刚好和屁股底下的牌凑一对(为什么剩下的最后一张刚好就是堆底?具体原因在下一章节的数学逻辑中再详解)

为什么在最关键的四步中都没有看见咒语?下面将会用到

第四步 设计丢牌条件

为了体现"共此时"的理念,在步骤三和最后一步之间再按条件丢些牌。

  • 步骤四:按条件丢牌,只要丢的牌数小于等于7(=8-1,8为之前算出的牌数),可以随意设计,越神秘越好,比如
    • 男生扔一张
    • 女生扔两张
    • 单身再扔一张
  • 步骤五:念咒语,每念一个字将牌从堆顶移动到堆底

咒语的作用就是消除丢牌对最后一步的影响,保证丢不同数量牌后,最后一步剩下的始终是堆底的那张。(具体原理在下一章节的数学逻辑中再详解)

第五步 再添加些没什么用的神秘步骤

步骤一和步骤二之间可以将任意数量的牌从堆顶移动到堆底。因为此时只要相对顺序不变,不影响最后结果。刘谦魔术的第一步就是按名字字数把牌从堆顶移到堆底,这一步其实没啥用。很多人说小尼失误是因为名字太长了,放错了,显然是不对的,即使数量错了,也不会影响魔术最终的成功。

步骤三和念咒语之前可以将任意数量的牌从堆顶移动到中间。因为此时底牌最重要,只要底牌不变就行。刘谦在藏牌到屁股底下后,让大家根据南北方人的不同,从牌顶往中间插不同数量的牌。小尼的失误其实就在这一步,他没有把牌插入中间而是放在了底部,把最为关键的底牌给换了,导致最后没有配对上。

恭喜你,此时你就拥有一个和刘谦咒语不同的 《守岁共此时》同款魔术了。相信你也准备好了更加烧脑的环节,理解魔术背后的数学逻辑。

魔术背后的数学逻辑

约瑟夫环:程序员的回忆杀

当刘谦在最后一步说 "好运留下来,烦恼丢出去" 时,立即就唤醒了我本科学习 Java 的记忆。在 "Java程序设计" 专业课期末考试的最后一题就是,"n个猴子围成一圈不断报数,奇数留下来,偶数淘汰,求最后留下来的猴子是第几个" 。估计不少程序员在初学编程时,都接触过类似的题目。leetcode 上这个题目叫做 破冰游戏

我这样的小菜鸡一般就写两个 for 循环模拟整个过程,大约几十行代码,沾沾自喜。然后惊讶地大神一行代码就解决了:

java 复制代码
//  n 为总数
// highestOneBit 是 Java 内置的方法,用于求小于等于 n 的最大2次幂
2*(n - Integer.highestOneBit(n))+1

也就说存在这么一个简单的公式,最后留下来的猴子编号,为 2*(n-小于等于n的最大2次幂)+1:

  • 2 个猴子,小于等于 2 的最大2次幂为 2,则最后留下来的为第 2*(2-2)+1=1
  • 7 个猴子,小于等于 7 的最大2次幂为 4,则最后留下来的为第 2*(7-4)+1=7
  • 15 个猴子,小于等于 15 的最大2次幂为 8,则最后留下来的为第 2*(15-8)+1=15

这是一个经典的数学问题,叫做"约瑟夫环"。这个公式乍看下比较神奇,细想一下其实很直观。 当猴子的数量为 2 的幂次时,每轮报数都会刚好减少一半并且回到第一个猴子,第一个猴子报的永远都是奇数,所以最后剩下的只能是第一个猴子:

对于一般情况,当有 (n-小于n的最小2次幂) 的猴子被淘汰时,就又回到了特殊情况,而且等价于特殊情况中1号猴子的,就是 2*(n-小于n的最小2次幂)+1 号:

构造方程

有了约瑟夫环的公式,我们就可以根据两个条件构造方程:

  • 根据操作步骤,推断最后剩下的牌是第几张,假设这个数字为 k
  • k 还必须为牌堆最后一张,因为我们将配对的牌留在了最底下

假设我们的牌数为 n,则撕开后的牌数为 2n ,因为我们会藏一张牌,所以参与约瑟夫环的牌就只有 2n-1 张, 假设 2n-1=2^m+b 。我们按照核心步骤推演一下 k

  • 步骤四会按条件丢牌,但是我们约定丢的牌数是不会超过 b 的,假设丢的牌数为 a ,所以最后参与约瑟夫环的牌数为 2n-1-a,可以写作 2n-1-a=2^m+b-a
  • 步骤五念咒语,假设咒语字数为 c ,会导致牌往前移动 c 位,因此约瑟夫环的结果需要 +c 才是牌原本的位置。
  • 最后一步约瑟夫环,参与的牌数为 2^m+b-a, 套用公式为 2(b-a)+1 ,再修正下咒语的影响,因此 k=2(b-a)+1+c=c+1+2b-2a

同时 k 还必须是牌堆的最后一张,因为此 k 必须是参与约瑟夫环牌数的整数倍,即 2n-1-a=2^m+b-a 的整数倍 。其中 a 是观众按条件任意丢弃的,无法控制,而 b 跟牌数相关,c 是咒语字数,都是魔术师可以控制的,因此我们要做到与 a 无关。从 k 的公式中我们可以看到 -2a,因此只能是 2^m+b-a 的 2 倍,进而列出本文最重要的方程:

化简得

因此咒语字数必须为2的幂次减一。根据前面的定义 2n-1=2^m+b,我们可以进一步求得牌数( 2^m 我们可以理解成比 c 小的最大的2的幂次):

b 必须小于 2^m,大于0,因此可以求得一个范围:

  • 下限:b 取 1 时,n=2^(m-1)+1
  • 上限:b 取 2^m-1 时,n=2^m,即 n 为比 c 小的最大的2的幂次

所以对于刘谦用 7 字咒语,3张牌也是可以的,但是按条件丢牌的牌数是不能超过 b 的,b 取得太小,步骤四的按条件丢牌就会受到限制。如果刘谦用3张牌,那么丢牌的数量就能超过 1,"男生丢一张,女生丢两张"这个设计就要翻车了,刘谦还是必须要用 4 张牌。

为了给丢牌留最大的空间,b 我们直接取最大值,此时的牌数刚好就是比 c 小的最大的2的幂次,丢牌的最大数量b就是牌数减1。

总结

思考了很久,但是最后的结论却非常简洁,这或许就是数学的魅力吧:

End

感谢阅读,希望本文能让您有所收获。文中的结论,除了经典的约瑟夫环理论外,其余方程都为笔者自行推导,引用请注明出处。如果看完还有困惑之处,也欢迎评论本文和我进一步交流,有问必回。

作者:元青

微信公众号 「技乐书香」

相关推荐
从以前41 分钟前
【算法题解】Bindian 山丘信号问题(E. Bindian Signaling)
开发语言·python·算法
不白兰1 小时前
[代码随想录23回溯]回溯的组合问题+分割子串
算法
御风@户外2 小时前
质数生成函数、质数判断备份
算法·acm
闻缺陷则喜何志丹2 小时前
【C++动态规划】1105. 填充书架|2104
c++·算法·动态规划·力扣·高度·最小·书架
Dong雨2 小时前
六大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序
数据结构·算法·排序算法
达帮主2 小时前
7.C语言 宏(Macro) 宏定义,宏函数
linux·c语言·算法
是十一月末2 小时前
机器学习之KNN算法预测数据和数据可视化
人工智能·python·算法·机器学习·信息可视化
chenziang12 小时前
leetcode hot100 路径总和
算法
lyx1426062 小时前
leetcode 3083. 字符串及其反转中是否存在同一子字符串
算法·leetcode·职场和发展
茶猫_2 小时前
力扣面试题 39 - 三步问题 C语言解法
c语言·数据结构·算法·leetcode·职场和发展