春晚刘谦魔术的模拟程序

2024-02-10 22:20

本文主要是介绍春晚刘谦魔术的模拟程序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

昨晚春晚上刘谦的两个魔术表演都非常精彩,尤其是第二个魔术,他演绎了经典的约瑟夫环问题!

什么是约瑟夫环问题?

约瑟夫环(Josephus problem)是一个经典的数学问题,最早由古罗马历史学家弗拉维奥·约瑟夫斯提出,但它的名字是在19世纪由德国数学家约瑟夫·乔瑟夫斯(Josef Stein)命名的。

问题的描述是这样的:假设有n个人(编号从1到n)站成一个圆圈,从第一个人开始报数,报到某个数字(例如k)的人就被杀死,然后从下一个人开始重新报数并继续这个过程,直到只剩下一个人留下来。

问题的关键是找出存活下来的那个人的编号。

结合扑克牌解释约瑟夫环问题

1、考虑最简单的情况

假设有2张牌,编号分别是1和2。

首先将1放到后面,扔掉2。剩下的就是最开始放在最上边的那张1。

2、稍微复杂一点的情况,牌的张数是2的n次方

比如有8张牌,编号分别是1、2、3、4、5、6、7、8。

第一轮会把2、4、6、8扔掉,剩下1、3、5、7按顺序放在后面,又退化成了4张牌的情况。

第二轮会把3、7扔掉,剩下1、5按顺序放在后面,又退化成了2张牌的情况。

第三轮把5扔掉,剩下1,就是最初在最前面的那张。

结论:如果牌的张数是2^n,最后剩下的一定是最开始放在牌堆顶的那张。

3、考虑任意的情况,牌的张数是2^n+m

比如牌的张数是11,等于8+3。把1放到后面,把2扔掉,把3放到后面,把4扔掉,把5放到后面,把6扔掉,现在剩下的编号序列是7、8、9、10、11、1、3、5,这又是8张牌的情况!最后一定剩下的是现在牌堆顶的7!

因此,只要提前知道牌的张数,就一定能马上推导出最终是剩下哪一张牌。一切的魔法都是数学!!都是算法!!

见证奇迹的时刻!魔术的流程

  1. 4张牌对折后撕开,就是8张,叠放在一起就是ABCDABCD。注意,ABCD四个数字是完全等价的。
  2. 根据名字字数,把顶上的牌放到下面,但怎么放都不会改变循环序列的相对位置。譬如2次,最后变成CDABCDAB;譬如3次,最后换成DABCDABC。但无论怎么操作,第4张和第8张牌都是一样的。
  3. 把顶上3张插到中间任意位置。这一步非常重要!因为操作完之后必然出现第1张和第8张牌是一样的!以名字两个字为例,可以写成BxxxxxxB(这里的x是其他和B不同的牌)。
  4. 拿掉顶上的牌放到一边,记为B。剩下的序列是xxxxxxB,一共7张牌。
  5. 南方人/北方人/不确定,分别拿顶上的1/2/3张牌插到中间,但是不会改变剩下7张牌是xxxxxxB的结果。
  6. 男生拿掉1张,女生拿掉2张。也就是男生剩下6张,女生剩下5张。分别是xxxxxB和xxxxB。
  7. 循环7次,把最顶上的放到最底下,男生和女生分别会是xxxxBx和xxBxx。
  8. 最后执行约瑟夫环过程!操作到最后只剩下1张。当牌数为6时(男生),剩下的就是第5张牌;当牌数为5时(女生),剩下的就是第3张牌。Bingo!就是第4步拿掉的那张牌!

下面是完整的 JavaScript 代码实现:

// 定义一个函数,用于把牌堆顶n张牌移动到末尾
function moveCardBack(n, arr) {// 循环n次,把队列第一张牌放到队列末尾for (let i = 0; i < n; i++) {const moveCard = arr.shift();  // 弹出队头元素,即第一张牌arr.push(moveCard);            // 把原队头元素插入到序列末尾}return arr;
}// 定义一个函数,用于把牌堆顶n张牌移动到中间的任意位置
function moveCardMiddleRandom(n, arr) {// 插入在arr中的的位置,随机生成一个idx// 这个位置必须是在n+1到arr.length-1之间const idx = Math.floor(Math.random() * (arr.length - n - 1)) + n + 1;// 执行插入操作const newArr = arr.slice(n, idx).concat(arr.slice(0, n)).concat(arr.slice(idx));return newArr;
}// 步骤1:初始化8张牌,假设为"ABCDABCD"
let arr = ["A", "B", "C", "D", "A", "B", "C", "D"];
console.log("步骤1:拿出4张牌,对折撕成8张,按顺序叠放。");
console.log("此时序列为:" + arr.join('') + "\n---");// 步骤2(无关步骤):名字长度随机选取,这里取2到5(其实任意整数都行)
const nameLen = Math.floor(Math.random() * 4) + 2;
// 把nameLen张牌移动到序列末尾
arr = moveCardBack(nameLen, arr);
console.log(`步骤2:随机选取名字长度为${nameLen},把第1张牌放到末尾,操作${nameLen}次。`);
console.log(`此时序列为:${arr.join('')}\n---`);// 步骤3(关键步骤):把牌堆顶三张放到中间任意位置
arr = moveCardMiddleRandom(3, arr);
console.log(`步骤3:把牌堆顶3张放到中间的随机位置。`);
console.log(`此时序列为:${arr.join('')}\n---`);// 步骤4(关键步骤):把最顶上的牌拿走
const restCard = arr.shift();  // 弹出队头元素
console.log(`步骤4:把最顶上的牌拿走,放在一边。`);
console.log(`拿走的牌为:${restCard}`);
console.log(`此时序列为:${arr.join('')}\n---`);// 步骤5(无关步骤):根据南方人/北方人/不确定,把顶上的1/2/3张牌插入到中间任意位置
// 随机选择1、2、3中的任意一个数字
const moveNum = Math.floor(Math.random() * 3) + 1;
arr = moveCardMiddleRandom(moveNum, arr);
console.log(`步骤5:我${moveNum === 1 ? '是南方人' : moveNum === 2 ? '是北方人' : '不确定自己是哪里人'},\
把${moveNum}张牌插入到中间的随机位置。`);
console.log(`此时序列为:${arr.join('')}\n---`);// 步骤6(关键步骤):根据性别男或女,移除牌堆顶的1或2张牌
const maleNum = Math.floor(Math.random() * 2) + 1;  // 随机选择1或2
for (let i = 0; i < maleNum; i++) {  // 循环maleNum次,移除牌堆顶的牌arr.shift();
}
console.log(`步骤6:我是${maleNum === 1 ? '男' : '女'}生,移除牌堆顶的${maleNum}张牌。`);
console.log(`此时序列为:${arr.join('')}\n---`);// 步骤7(关键步骤):把顶部的牌移动到末尾,执行7次
arr = moveCardBack(7, arr);
console.log(`步骤7:把顶部的牌移动到末尾,执行7次`);
console.log(`此时序列为:${arr.join('')}\n---`);// 步骤8(关键步骤):执行约瑟夫环过程。把牌堆顶一张牌放到末尾,再移除一张牌,直到只剩下一张牌。
console.log(`步骤8:把牌堆顶一张牌放到末尾,再移除一张牌,直到只剩下一张牌。`);
while (arr.length > 1) {const luck = arr.shift();  // 好运留下来arr.push(luck);console.log(`好运留下来:${luck}\t\t此时序列为:${arr.join('')}`);const sadness = arr.shift();  // 烦恼都丢掉console.log(`烦恼都丢掉:${sadness}\t\t此时序列为:${arr.join('')}`);
}
console.log(`---\n最终结果:剩下的牌为${arr[0]},步骤4中留下来的牌也是${restCard}`);

这段代码实现了昨晚春晚上刘谦的第二个魔术表演的过程,并提供了详细的解释。享受魔术的魅力吧!

image-20240210161329783

image-20240210161339317

这篇关于春晚刘谦魔术的模拟程序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/698173

相关文章

魔术方法介绍

目录 一、基本介绍 1、什么是魔术方法 2、常见的魔术方法 二、__str__ 1、基本介绍 2、应用实例:请输出Monster对象的属性信息 三、__eq__ 1、基本介绍 2、应用实例 四、其它几个魔术方法 1、其它魔术方法 2、应用实例 参考文档:3. 数据模型 — Python 3.12.5 文档 一、基本介绍 1、什么是魔术方法 1)在Pyth

转载:提示行魔术

IBM Bluemix 点击按钮,开始云上的开发! 开始您的试用 登录  |  注册 IBM 技术主题 软件下载 社区 技术讲座 搜索 developerWorks 打印本页面 用电子邮件发送本页面 新浪微博 人人网 腾讯微博 搜狐微博 网易微博 Digg Facebook Twitter Delicious Linked In

python所有的魔术方法钩子函数

C.__init__(self[, arg1, ...]) 构造器(带一些可选的参数) C.__new__(self[, arg1, ...]) 构造器(带一些可选的参数)通常用在设置不变数据类型的子类。 C.__del__(self) 析构器 C.__str__(self) 可打印的字符输出;内建str()及print 语句 C.__repr__(self) 运行时的字符

Linux中SysRq的使用(魔术键)

魔术键:Linux Magic System Request Key Hacks vim kernel/power/poweroff.c当Linux 系统不能正常响应用户请求时, 可以使用SysRq小工具控制Linux. 一 SysRq的启用与关闭 要想启用SysRq, 需要在配置内核时设置Magic SysRq key (CONFIG_MAGIC_SYSRQ)为Y. 对于支持SysRq的内核,

Python中类的魔术方法之--反射

文章目录 Python中类的魔术方法之--反射概述与反射相关的内建函数反射相关的魔术方法`__getattr__(self,item)`方法`__setattr__(self,item)`方法`__delattr__(self,item)`方法`__getattribute__(self,item)`方法 附加:访问流程图 Python中类的魔术方法之–反射 概述 运行时,区

Python中类的魔术方法之上下文管理

文章目录 Python中类的魔术方法之上下文管理上下文管理上下文管理对象与管理的安全上下文应用场景contextlib.countextmanager上下文管理装饰器 Python中类的魔术方法之上下文管理 上下文管理 文件IO操作可以对文件对象使用上下文管理,使用with…as语法。 示例: with open("text") as f:pass 仿照上面例子写一个自

Python中类的特殊属性与魔术方法

文章目录 Python中类的特殊属性与魔术方法特殊的属性查看属性特殊函数 魔术方法实例化可视化可哈希(hash)与等等运算符(==)bool运算符重载1.比较运算符重载2.算术运算符重载3.反向运算符4.赋值运算符的重载运算符重载的应用场景functools.total_ordering装饰器 容器相关方法可调用对象 Python中类的特殊属性与魔术方法 特殊的属性 属性含

php面向对象中的魔术常量

1。__LINE__  返回文件中的当前行号。   2。__FILE__  返回文件的完整路径和文件名。如果用在包含文件中,则返回包含文件名。自 PHP 4.0.2 起,__FILE__ 总是包含一个绝对路径,而在此之前的版本有时会包含一个相对路径。   3。__FUNCTION__  返回函数名称(PHP 4.3.0 新加)。自 PHP 5 起本常量返回该函数被定义时的名字(区

php面向对象中的魔术函数

1。__construct()  实例化对象时被调用,  当__construct和以类名为函数名的函数同时存在时,__construct将被调用,另一个不被调用。   2。__destruct()  当删除一个对象或对象操作终止时被调用。   3。__call()  对象调用某个方法,  若方法存在,则直接调用;  若不存在,则会去调用__call函数。   4。__ge

mysql数据库中模拟程序循环执行sql命令快速造数命令

解决复杂问题不可能通过一个 SQL 语句完成,我们需要执行多个 SQL 操作。流程控制语句的作用就是控 制存储过程中 SQL 语句的执行顺序,是我们完成复杂操作必不可少的一部分。只要是执行的程序,流程 就分为三大类: 顺序结构 :程序从上往下依次执行 分支结构 :程序按条件进行选择执行,从两条或多条路径中选择一条执行 循环结构 :程序满足一定条件下,重复执行一组语句 针对于MySQL 的流程控制语