用Python来实现2024年春晚刘谦魔术

简介

这是新春的第一篇,今天早上睡到了自然醒,打开手机刷视频就被刘谦的魔术所吸引,忍不住用编程去模拟一下这个过程。

首先,声明的一点,大年初一不学习,所以这其中涉及的数学原理约瑟夫环大家可以找找其他的教程看看,我这块只是复现它魔术里面的每个步骤。

魔术的步骤

总而言之,可以分为以下8个步骤:

Step 1: 将四张4张牌撕成两半,直接将两堆叠放;
Step 2: 假设姓名为n个字,重复n次,将堆在最上的牌放到最下面;
Step 3: 将牌堆最上的3张拿出,不改变顺序,并随机插入牌堆中间;
Step 4: 将牌堆最上方的牌拿走,放在一旁;
Step 5: 按照南/北/不知道是南或者北方地区,判断自己属于哪一地区,并分别将牌堆最上的1/2/3,不改变顺序,并随机插入牌堆中间;
Step 6: 按性别男/女,从牌堆最上方拿走1/2张牌,一边念口诀:“见证奇迹的时刻”,每念一个字,将牌堆最上方的牌放到牌堆最下;
Step 7: 念口诀“好运留下米”时,将牌堆最上的牌放到牌堆最下;念“烦恼扔出去”时,将牌堆最上方的牌移除。重复这两句口诀,直到手中只有一张牌;
Step 8: 最后留下的牌和Step 4拿走的牌是一样的。

过程拆开分来其实就是对列表进行一个简单的操作了

用python实现其中的过程

0. 模拟扑克牌打乱并抽取的过程;

import random
import itertools
import copy
# 定义扑克牌
suits = ['红桃', '方块', '梅花', '黑桃']
ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
jokers = ['小王', '大王']
deck_of_cards = list(itertools.product(suits, ranks)) + jokers
random.shuffle(deck_of_cards)       # 模拟打乱的操作
print(f"随机生成的{len(deck_of_cards)}扑克牌:", deck_of_cards)
selected_cards = random.sample(deck_of_cards, 4)
print("随机抽取其中的四张牌:", selected_cards)

随机抽取其中的四张牌: [('红桃', '9'), ('黑桃', '8'), ('黑桃', 'A'), ('黑桃', 'K')]

1. 将四张4张牌撕成两半,直接将两堆叠放;

def split_and_stack(cards):
    cards_copy = copy.copy(cards)
    merged_cards = cards + cards_copy
    return merged_cards

split_cards = split_and_stack(selected_cards)
print("撕成两半后堆叠:", split_cards)

撕成两半后堆叠: [('红桃', '9'), ('黑桃', '8'), ('黑桃', 'A'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', '8'), ('黑桃', 'A'), ('黑桃', 'K')]

2. 假设姓名为n个字,重复n次,将堆在最上的牌放到最下面;

def repeat_name(cards, name):
    name_length = len(name)
    for _ in range(name_length):
        # 取出堆在最上的牌,放到最下面
        top_card = cards.pop(0)  
        cards.append(top_card) 
    return cards

split_cards_repeated = repeat_name(split_cards, name)
print(f"{name} 重复姓名字数次后的牌堆:", split_cards_repeated)

夏天是冰红茶 重复姓名字数次后的牌堆: [('黑桃', 'A'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', '8'), ('黑桃', 'A'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', '8')]

3. 将牌堆最上的3张拿出,不改变顺序,并随机插入牌堆中间;

def take_top_and_insert(cards):
    top_three_cards = cards[:3]  # 取出最上面的3张牌
    remaining_cards = cards[3:]  # 剩下的牌
    insert_index = random.randint(1, len(remaining_cards))
    shuffled_cards = remaining_cards[:insert_index] + top_three_cards + remaining_cards[insert_index:]
    return shuffled_cards

shuffled_cards = take_top_and_insert(split_cards_repeated)
print("牌堆最上的3张拿出,随机插入后的牌堆:", shuffled_cards)

牌堆最上的3张拿出,随机插入后的牌堆: [('黑桃', '8'), ('黑桃', 'A'), ('黑桃', 'A'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', '8')]

4. 将牌堆最上方的牌拿走,放在一旁;

def take_top_card(cards):
    top_card = cards.pop(0)  # 取出最上方的牌
    return top_card

top_card = take_top_card(shuffled_cards)
print("拿走的牌:", top_card)
print("剩余的牌:", shuffled_cards)

拿走的牌: ('黑桃', '8')
剩余的牌: [('黑桃', 'A'), ('黑桃', 'A'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', '8')] 

5. 按照南/北/不知道是南或者北方地区,判断自己属于哪一地区,并分别将牌堆最上的1/2/3,不改变顺序,并随机插入牌堆中间;

def insert_cards_based_on_region(cards, region):
    if region == "南":
        insert_count = 1
    elif region == "北":
        insert_count = 2
    else:
        insert_count = 3

    top = cards[:insert_count]
    remaining_cards = cards[insert_count:]
    insert_index = random.randint(0, len(remaining_cards)-1)
    shuffled_cards = remaining_cards[:insert_index] + top + remaining_cards[insert_index:]

    return shuffled_cards


shuffled_cards_region = insert_cards_based_on_region(shuffled_cards, region)
print(f"{region}方地区插入后的牌堆:", shuffled_cards_region)

南方地区插入后的牌堆: [('黑桃', 'A'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', 'K'), ('黑桃', 'A'), ('红桃', '9'), ('黑桃', '8')] 

6. 按性别男/女,从牌堆最上方拿走1/2张牌,一边念口诀:“见证奇迹的时刻”,每念一个字,将牌堆最上方的牌放到牌堆最下;

def take_and_chant(cards, gender, chant="见证奇迹的时刻"):
    take_count = 0
    if gender == "男":
        take_count = 1
    elif gender == "女":
        take_count = 2
    else:
        print("未知性别")

    remaining_cards = cards[take_count:]  # 剩下的牌
    print(remaining_cards)
    # 念口诀过程
    for c in chant:
        remaining_cards.append(remaining_cards.pop(0))  # 将最上方的牌放到牌堆最下

    return remaining_cards

remaining_cards= take_and_chant(shuffled_cards_region, gender, chant)
print(f"剩余的牌堆:", remaining_cards)

[('黑桃', 'K'), ('红桃', '9'), ('黑桃', 'K'), ('黑桃', 'A'), ('红桃', '9'), ('黑桃', '8')]
剩余的牌堆: [('红桃', '9'), ('黑桃', 'K'), ('黑桃', 'A'), ('红桃', '9'), ('黑桃', '8'), ('黑桃', 'K')] 

7/8. 念口诀“好运留下米”时,将牌堆最上的牌放到牌堆最下;念“烦恼扔出去”时,将牌堆最上方的牌移除。重复这两句口诀,直到手中只有一张牌;最后留下的牌和Step 4拿走的牌是一样的。

def chant_and_modify(cards):
    iter = 1
    while len(cards) > 1:
        chant_good_luck = "好运留下米"
        chant_throw_away = "烦恼扔出去"
        print(f"\n第{iter}轮口诀开始:")
        cards.append(cards.pop(0))
        print(f"口诀{chant_good_luck}结束后手上的牌:", cards)
        cards.pop(0)
        print(f"口诀{chant_throw_away}结束后手上的牌:", cards)
        iter += 1

    return cards[0]

final_card = chant_and_modify(remaining_cards)
print(f"\n最终留下的牌:{final_card}, Step 4:{top_card}")

第1轮口诀开始:
口诀好运留下米结束后手上的牌: [('黑桃', 'K'), ('黑桃', 'A'), ('红桃', '9'), ('黑桃', '8'), ('黑桃', 'K'), ('红桃', '9')]
口诀烦恼扔出去结束后手上的牌: [('黑桃', 'A'), ('红桃', '9'), ('黑桃', '8'), ('黑桃', 'K'), ('红桃', '9')]

第2轮口诀开始:
口诀好运留下米结束后手上的牌: [('红桃', '9'), ('黑桃', '8'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', 'A')]
口诀烦恼扔出去结束后手上的牌: [('黑桃', '8'), ('黑桃', 'K'), ('红桃', '9'), ('黑桃', 'A')]

第3轮口诀开始:
口诀好运留下米结束后手上的牌: [('黑桃', 'K'), ('红桃', '9'), ('黑桃', 'A'), ('黑桃', '8')]
口诀烦恼扔出去结束后手上的牌: [('红桃', '9'), ('黑桃', 'A'), ('黑桃', '8')]

第4轮口诀开始:
口诀好运留下米结束后手上的牌: [('黑桃', 'A'), ('黑桃', '8'), ('红桃', '9')]
口诀烦恼扔出去结束后手上的牌: [('黑桃', '8'), ('红桃', '9')]

第5轮口诀开始:
口诀好运留下米结束后手上的牌: [('红桃', '9'), ('黑桃', '8')]
口诀烦恼扔出去结束后手上的牌: [('黑桃', '8')]

最终留下的牌:('黑桃', '8'), Step 4:('黑桃', '8')

完整的代码

import random
import itertools
import copy
# 定义扑克牌
suits = ['红桃', '方块', '梅花', '黑桃']
ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
jokers = ['小王', '大王']
deck_of_cards = list(itertools.product(suits, ranks)) + jokers
random.shuffle(deck_of_cards)       # 模拟打乱的操作
print(f"随机生成的{len(deck_of_cards)}扑克牌:", deck_of_cards)
selected_cards = random.sample(deck_of_cards, 4)
print("随机抽取其中的四张牌:", selected_cards)


# 模拟性别为男的情况
name = "夏天是冰红茶"
gender = "男"
chant = "见证奇迹的时刻"
region = "南"

# step 1: 将四张4张牌撕成两半,直接将两堆叠放;
def split_and_stack(cards):
    cards_copy = copy.copy(cards)
    merged_cards = cards + cards_copy
    return merged_cards

split_cards = split_and_stack(selected_cards)
print("撕成两半后堆叠:", split_cards)

# Step 2: 设你的姓名为n个字,重复n次,将堆在最上的牌放到最下面;
def repeat_name(cards, name):
    name_length = len(name)
    for _ in range(name_length):
        # 取出堆在最上的牌,放到最下面
        top_card = cards.pop(0)
        cards.append(top_card)
    return cards

split_cards_repeated = repeat_name(split_cards, name)
print(f"{name} 重复姓名字数次后的牌堆:", split_cards_repeated)

# Step 3: 将牌堆最上的3张拿出,不改变顺序,并随机插入牌堆中间
def take_top_and_insert(cards):
    top_three_cards = cards[:3]  # 取出最上面的3张牌
    remaining_cards = cards[3:]  # 剩下的牌
    insert_index = random.randint(1, len(remaining_cards))
    shuffled_cards = remaining_cards[:insert_index] + top_three_cards + remaining_cards[insert_index:]
    return shuffled_cards

shuffled_cards = take_top_and_insert(split_cards_repeated)
print("牌堆最上的3张拿出,随机插入后的牌堆:", shuffled_cards)

# Step 4: 将牌堆最上方的牌拿走,放在一旁
def take_top_card(cards):
    top_card = cards.pop(0)  # 取出最上方的牌
    return top_card

top_card = take_top_card(shuffled_cards)
print("拿走的牌:", top_card)
print("剩余的牌:", shuffled_cards)

# Step 5: 按照南/北/不知道是南或者北方地区,判断自己属于哪一地区,并分别将牌堆最上的1/2/3,不改变顺序,并随机插入牌堆中间
def insert_cards_based_on_region(cards, region):
    if region == "南":
        insert_count = 1
    elif region == "北":
        insert_count = 2
    else:
        insert_count = 3

    top = cards[:insert_count]
    remaining_cards = cards[insert_count:]
    insert_index = random.randint(0, len(remaining_cards)-1)
    shuffled_cards = remaining_cards[:insert_index] + top + remaining_cards[insert_index:]

    return shuffled_cards


shuffled_cards_region = insert_cards_based_on_region(shuffled_cards, region)
print(f"{region}方地区插入后的牌堆:", shuffled_cards_region)


# Step 6: 按性别男/女,从牌堆最上方拿走1/2张牌,一边念口诀:“见证奇迹的时刻”,每念一个字,将牌堆最上方的牌放到牌堆最下。
def take_and_chant(cards, gender, chant="见证奇迹的时刻"):
    take_count = 0
    if gender == "男":
        take_count = 1
    elif gender == "女":
        take_count = 2
    else:
        print("未知性别")

    remaining_cards = cards[take_count:]  # 剩下的牌
    print(remaining_cards)
    # 念口诀过程
    for c in chant:
        remaining_cards.append(remaining_cards.pop(0))  # 将最上方的牌放到牌堆最下

    return remaining_cards


remaining_cards= take_and_chant(shuffled_cards_region, gender, chant)
print(f"剩余的牌堆:", remaining_cards)

# Step 7: 念口诀“好运留下米”时,将牌堆最上的牌放到牌堆最下;念“烦恼扔出去”时,将牌堆最上方的牌移除。重复这两句口诀,直到手中只有一张牌;
def chant_and_modify(cards):
    iter = 1
    while len(cards) > 1:
        chant_good_luck = "好运留下米"
        chant_throw_away = "烦恼扔出去"
        print(f"\n第{iter}轮口诀开始:")
        cards.append(cards.pop(0))
        print(f"口诀{chant_good_luck}结束后手上的牌:", cards)
        cards.pop(0)
        print(f"口诀{chant_throw_away}结束后手上的牌:", cards)
        iter += 1

    return cards[0]

# Step 8: 最后留下的牌和Step 4拿走的牌是一样的。
final_card = chant_and_modify(remaining_cards)
print(f"\n最终留下的牌:{final_card}, Step 4:{top_card}")

大家可以自己去试一试,在步骤6后男生拿走的牌总是会在对应的第5位,女生拿走的牌总是会在对应的第3位。

结语

其实说实话,这种数学魔术在我小时候买的书里就曾经看到过许多。虽然现在了解了其中的数学原理,但当时的惊奇与欢乐感觉依然难以忘怀。刘谦老师在表演中展现了非凡的技艺,不仅仅是数学的巧妙运用,更是他善于抓住观众的好奇心,创造出让人难以置信的奇迹。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/381229.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL笔记:约束条件

MYSQL笔记:约束条件 主键约束 不能为空,值必须是不同的(唯一性) 一个表只能修饰一个主键 PRIMARY KEY自增约束 AUTO_INCREMENT唯一键约束 可以为空 unique非空约束 not null 默认值约束 default 外键约束 foreign key …

re:从0开始的CSS学习之路 3. CSS三大特性

0. 写在前面 很多的学习其实并不知道在学什么,学一个新东西学着学着就变成了抄代码,背概念。把看视频学习变成了一个赶进度的任务,到头来只学到了一些皮毛。 文章目录 0. 写在前面1. CSS三大特性——层叠性2. CSS三大特性——优先级3. CSS三…

SpringBoot框架入门指南

文章目录 SpringBoot的特点Spring,SpringBoot的区别SpringBoot常用注解标签SpringBoot概述SpringBoot简单Demo搭建读取配置文件的内容 SpringBoot自动配置Condition自定义beanSpringBoot常用注解原理EnableAutoConfiguration SpringBoot监听机制SpringBoot启动流程分…

【JavaScript 漫游】【013】Date 对象知识点摘录

文章简介 本文为【JavaScript 漫游】专栏的第 013 篇文章,记录了 JS 语言中 Date 对象的重要知识点。 普通函数的用法构造函数的用法日期的运算静态方法,包括:Date.now()、Date.parse() 和 Date.UTC()实例方法,包括:…

以太网协议

以太网 .以太网协议格式🎨目的地址,源地址mac地址格式 . 以太网协议格式🎨 目的地址,源地址 此处的地址,叫做mac地址(物理地址),长度是6个字节 mac地址的作用也是用来区分不同的主机 IP地址的长度是4字节 IP地址负责网络层(整体)转发,mac地址负责数据链…

K8S之运用节点选择器指定Pod运行的节点

node节点选择器的使用 使用场景实践使用nodeName使用nodeSelectornodeName和nodeSelector混合使用1、设置了nodeName 和 设置 Node上都不存在的标签。看调度情况2、设置nodeName 为node1 和 设置 node2上才有的标签。看调度情况 实践总结 使用场景 默认情况,在创建…

计算机网络——05Internet结构和ISP

Internet结构和ISP 互连网络结构:网络的网络 端系统通过接入ISPs连接到互连网 住宅、公司和大学的ISPs 接入ISPs相应的必须是互联的 因此任何2个端系统可相互发送分组到对方 导致的“网络的网络”非常复杂 发展和演化是通过经济的和国家的政策来驱动的 问题&…

Windows下搭建Redis Sentinel

下载安装程序 下载Redis关于Windows安装程序,下载地址 下载成功后进行解压,解压如下: 配置redis和sentinel 首先复制三份redis.windows.conf,分别命名为:redis.6379.conf、redis.6380.conf、redis.6381.conf&…

多线程JUC:等待唤醒机制(生产者消费者模式)

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习 🌌上期文章:多线程&JUC:解决线程安全问题——synchronized同步代码块、Lock锁 📚订阅专栏:多线程&am…

【C语言】深入理解指针

目录 1.字符指针 2.指针数组 3.数组指针 4.数组传参与指针传参 一维数组传参 二维数组传参 一级指针传参 二级指针传参 5.函数指针 6.函数指针数组 7.指向函数指针数组的指针(了解即可) 8.回调函数 回调函数的应用:库函数qsort …

Codeforces Round 923 (Div. 3) E. Klever Permutation (Java)

比赛链接:Round 923 (Div. 3) EE题传送门:E. Klever Permutation 题目:E. Klever Permutation 样例 #1 样例输入 #1 5 2 2 3 2 10 4 13 4 7 4样例输出 #1 2 1 1 3 2 1 8 4 10 2 7 5 9 3 6 4 10 1 13 5 9 2 12 6 8 3 11 7 1 6 3 7 2 5 4分…

【06】C++ 模板初阶

文章目录 🌈 Ⅰ 泛型编程🌈 Ⅱ 函数模板1. 函数模板概念2. 函数模板格式 🌈 Ⅰ 泛型编程 1. 泛型编程引入 假设当前要实现交换两个变量的功能,那么就得根据实参的数据类型来对该函数进行重载。重载的函数只是数据类型不同而已&a…

逐行拆解Guava限流器RateLimiter

逐行拆解Guava限流器RateLimiter 常见限流算法 计数器法 设置一个时间窗口内允许的最大请求量,如果当前窗口请求数超过这个设定数量,则拒绝该窗口内之后的请求。 关键词:时间窗口,计数器。 举个例子,我们设置1秒钟…

解决“使用Edge浏览器每次鼠标点击会出现一个黑色边框”的问题

目录 一 问题描述 二 解决方案 三 方案来源 四 参考资料 & AI工具 一 问题描述 为了方便进行收藏夹同步,开始从Chrome浏览器切换到Edge浏览器。在使用Edge浏览器过程中发现“每次鼠标点击会出现一个黑色边框”(效果如下图所示)&#…

基于物联网的实时数据分析(简单介绍)

在当今这个信息化、数字化飞速发展的时代,物联网(Internet of Things, IoT)和实时数据分析成为了技术革新的两大支柱。对于刚入行的新手来说,理解这两个概念及其相互作用不仅是迈入这一领域的第一步,更是掌握未来技术趋…

Android 粒子喷泉动效

一、前言: 在学习open gl es实现动效的时候,打算回顾了一下用普通的2D坐标系实现粒子效果和 open gl 3d 坐标系的区别,以及难易程度,因此本篇以Canvas 2D坐标系实现了一个简单的demo。 粒子动效原理: 粒子动效本质上…

Springboot 整合 Elasticsearch(二):使用HTTP请求来操作ES

📁前情提要:Springboot整合Elasticsearch(一):Linux下安装 Elasticsearch 8.x 目录 一、使用 elasticsearch-head 插件连接 1、下载压缩包 2、在 chrome 浏览器中添加扩展程序 3、修改IP地址,点击连接 …

Excel+VBA处理高斯光束

文章目录 1 图片导入与裁剪2 获取图片数据3 数据拟合 1 图片导入与裁剪 插入图片没什么好说的,新建Excel,【插入】->【图片】。 由于图像比较大,所以要对数据进行截取,选中图片之后,点击选项卡右端的【图片格式】…

Postgresql 的编译安装与包管理安装, 全发行版 Linux 通用

博客原文 文章目录 实验环境信息编译安装获取安装包环境依赖编译安装安装 contrib 下工具代码 创建用户创建数据目录设置开机自启动启动数据库常用运维操作 apt 安装更新源安装 postgresql开机自启修改配置修改密码 实验环境信息 Ubuntu 20.04Postgre 16.1 编译安装 获取安装…

HiveSQL——不使用union all的情况下进行列转行

参考文章: HiveSql一天一个小技巧:如何不使用union all 进行列转行_不 union all-CSDN博客文章浏览阅读881次,点赞5次,收藏10次。本文给出一种不使用传统UNION ALL方法进行 行转列的方法,其中方法一采用了concat_wsposexplode()方…
最新文章