用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位。
结语
其实说实话,这种数学魔术在我小时候买的书里就曾经看到过许多。虽然现在了解了其中的数学原理,但当时的惊奇与欢乐感觉依然难以忘怀。刘谦老师在表演中展现了非凡的技艺,不仅仅是数学的巧妙运用,更是他善于抓住观众的好奇心,创造出让人难以置信的奇迹。
以上就是用Python实现2024年春晚刘谦魔术的详细内容,更多关于Python春晚刘谦魔术的资料请关注脚本之家其它相关文章!