今年的央视春晚确实有点看头,要不是小尼的那个失误,这个节目就没有这么开心。
刘谦的这个魔术,我总觉得是个数学问题,就安奈不住自己,忍不住用编程去模拟一下这个过程。正好用python复现这个问题。初一构思,初三今天一上午实现。
数学原理约瑟夫环问题,要用到迭代的一些东西,其他的不做过多解释。使用对应的算法代码解决就行。
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拿走的牌是一样的。
过程拆开,其实很简单,8张被撕成两半的牌组成的牌组,就是对列表进行一个简单的操作了。(どろ モンスターカード)
抱歉走错片场了^_^
random(用于随机使用),itertools(用于最后的迭代使用),copy(用于复制牌组)
# 导入所需的库
import random
import itertools
import copy
生成一幅完整的扑克牌牌组。然后直接洗牌(random),随机抽取4张牌。然后获取用户名,性别,地区。定义口诀名为:‘见证奇迹的时刻’。
# 定义一个名为CardGame的类
class CardGame:
# 定义初始化函数
def __init__(self):
# 定义四种花色
self.suits = ['红桃', '方块', '梅花', '黑桃']
# 定义十三种牌面
self.ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
# 定义两种大小王
self.jokers = ['小王', '大王']
# 生成一副完整的牌
self.deck_of_cards = list(itertools.product(self.suits, self.ranks)) + self.jokers
# 打乱这副牌
random.shuffle(self.deck_of_cards)
# 随机抽取四张牌
self.selected_cards = random.sample(self.deck_of_cards, 4)
# 打印出抽取的四张牌
print("随机抽取其中的四张牌:", self.selected_cards)
# 获取用户输入的姓名
self.name = input("请输入你的姓名:")
# 获取用户输入的性别
self.gender = input("请输入你的性别(男/女):")
# 获取用户输入的地区
self.region = input("请输入你所在的地区(南/北):")
# 定义口诀
self.chant = "见证奇迹的时刻"
用copy方法,把已经获得的牌组模拟撕成两半,然后进行堆叠,获得新的牌组
# 定义一个函数,将牌撕成两半后堆叠
def split_and_stack(self, cards):
# 复制一份牌
cards_copy = copy.copy(cards)
# 将原牌和复制的牌堆叠在一起
merged_cards = cards + cards_copy
# 返回堆叠后的牌
return merged_cards
首先就是要把姓名的长度转换成次数,然后利用循环机制进行重复。这里注意需要将堆在最上的牌放到最下面。
# 定义一个函数,重复姓名字数次后的牌堆
def repeat_name(self, cards, name):
# 获取姓名的长度
name_length = len(name)
# 根据姓名的长度,重复相应的次数
for _ in range(name_length):
# 取出牌堆最上面的一张牌
top_card = cards.pop(0)
# 将这张牌放到牌堆的最下面
cards.append(top_card)
# 返回处理后的牌堆
return cards
将牌堆最上的3张拿出,不改变顺序,并随机插入牌堆中间
这里用列表的方式进行操作。
# 定义一个函数,将牌堆最上的3张拿出,随机插入到牌堆中
def take_top_and_insert(self, cards):
# 取出牌堆最上的3张牌
top_three_cards = cards[:3]
# 获取剩余的牌
remaining_cards = cards[3:]
# 随机选择一个插入的位置
insert_index = random.randint(1, len(remaining_cards))
# 将3张牌插入到随机选择的位置
shuffled_cards = remaining_cards[:insert_index] + top_three_cards + remaining_cards[insert_index:]
# 返回处理后的牌堆
return shuffled_cards
将牌堆最上方的牌拿走,放在一旁。这张牌就是要实现对比的那个。(就是坐在屁股下的那张)
# 定义一个函数,取出牌堆最上的一张牌
def take_top_card(self, cards):
# 取出牌堆最上的一张牌
top_card = cards.pop(0)
# 返回这张牌
return top_card
按照南/北/不知道是南或者北方地区,判断自己属于哪一地区,并分别将牌堆最上的1/2/3,不改变顺序,并随机插入牌堆中间。
# 定义一个函数,根据地区插入牌
def insert_cards_based_on_region(self, cards, region):
# 如果地区是南方,插入1张牌
if region == "南":
insert_count = 1
# 如果地区是北方,插入2张牌
elif region == "北":
insert_count = 2
# 如果地区既不是南方也不是北方,插入3张牌
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
按性别男/女,从牌堆最上方拿走1/2张牌,一边念口诀:“见证奇迹的时刻”,每念一个字,将牌堆最上方的牌放到牌堆最下。
# 定义一个函数,根据性别取牌和念口诀
def take_and_chant(self, cards, gender, chant="见证奇迹的时刻"):
# 如果性别是男,取1张牌
if gender == "男":
take_count = 1
# 如果性别是女,取2张牌
elif gender == "女":
take_count = 2
# 如果性别既不是男也不是女,打印"未知性别"
else:
print("未知性别")
# 获取剩余的牌
remaining_cards = cards[take_count:]
# 根据口诀的长度,重复相应的次数
for c in chant:
# 取出牌堆最上面的一张牌
remaining_cards.append(remaining_cards.pop(0))
# 返回处理后的牌堆
return remaining_cards
念口诀“好运留下来”时,将牌堆最上的牌放到牌堆最下;念“烦恼扔出去”时,将牌堆最上方的牌移除。重复这两句口诀,直到手中只有一张牌。
# 定义一个函数,念口诀并修改牌堆
def chant_and_modify(self, cards):
# 初始化轮数
iter = 1
# 当牌堆中还有多于1张牌时,继续念口诀和修改牌堆
while len(cards) > 1:
# 定义口诀
stay_good_luck = "好运留下来"
bad_throw_away = "烦恼扔出去"
# 打印开始念口诀的轮数
print(f"\n第{iter}轮口诀开始:")
# 取出牌堆最上面的一张牌,放到牌堆的最下面
cards.append(cards.pop(0))
# 打印念完口诀后的牌堆
print(f"口诀{stay_good_luck}结束后手上的牌:", cards)
# 取出牌堆最上面的一张牌
cards.pop(0)
# 打印念完口诀后的牌堆
print(f"口诀{bad_throw_away}结束后手上的牌:", cards)
# 轮数加1
iter += 1
# 返回最后剩下的一张牌
return cards[0]
9、定义主函数,实现整个过程
# 定义一个函数,开始游戏
def play(self):
# 将牌撕成两半后堆叠
split_cards = self.split_and_stack(self.selected_cards)
# 打印撕成两半后堆叠的牌
print("撕成两半后堆叠", split_cards)
# 重复姓名字数次后的牌堆
split_cards_repeated = self.repeat_name(split_cards, self.name)
# 打印重复姓名字数次后的牌堆
print(f"{self.name}重复姓名字数次后的牌堆:", split_cards_repeated)
# 将牌堆最上的3张拿出,随机插入到牌堆中
shuffled_cards = self.take_top_and_insert(split_cards_repeated)
# 打印处理后的牌堆
print("牌堆最上的3张拿出,随机插入后的牌堆:", shuffled_cards)
# 取出牌堆最上的一张牌
top_card = self.take_top_card(shuffled_cards)
# 打印取出的牌
print("拿走的牌:", top_card)
# 打印剩余的牌
print("剩余的牌:", shuffled_cards)
# 根据地区插入牌
shuffled_cards_region = self.insert_cards_based_on_region(shuffled_cards, self.region)
# 打印处理后的牌堆
print(f"{self.region}方地区插入后的牌堆:", shuffled_cards_region)
# 根据性别取牌和念口诀
remaining_cards = self.take_and_chant(shuffled_cards_region, self.gender, self.chant)
# 打印剩余的牌堆
print(f"剩余的牌堆:", remaining_cards)
# 念口诀并修改牌堆
final_card = self.chant_and_modify(remaining_cards)
# 打印最后剩下的一张牌
print(f"\n最终留下的牌{final_card},Step 4:{top_card}")
整个类就实现完成了。
然后创建一个CardGame的实例,就可以开始游戏了。
最终,完整代码送上!
# 导入所需的库
import random
import itertools
import copy
# 定义一个名为CardGame的类
class CardGame:
# 定义初始化函数
def __init__(self):
# 定义四种花色
self.suits = ['红桃', '方块', '梅花', '黑桃']
# 定义十三种牌面
self.ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
# 定义两种大小王
self.jokers = ['小王', '大王']
# 生成一副完整的牌
self.deck_of_cards = list(itertools.product(self.suits, self.ranks)) + self.jokers
# 打乱这副牌
random.shuffle(self.deck_of_cards)
# 随机抽取四张牌
self.selected_cards = random.sample(self.deck_of_cards, 4)
# 打印出抽取的四张牌
print("随机抽取其中的四张牌:", self.selected_cards)
# 获取用户输入的姓名
self.name = input("请输入你的姓名:")
# 获取用户输入的性别
self.gender = input("请输入你的性别(男/女):")
# 获取用户输入的地区
self.region = input("请输入你所在的地区(南/北):")
# 定义口诀
self.chant = "见证奇迹的时刻"
# 定义一个函数,将牌撕成两半后堆叠
def split_and_stack(self, cards):
# 复制一份牌
cards_copy = copy.copy(cards)
# 将原牌和复制的牌堆叠在一起
merged_cards = cards + cards_copy
# 返回堆叠后的牌
return merged_cards
# 定义一个函数,重复姓名字数次后的牌堆
def repeat_name(self, cards, name):
# 获取姓名的长度
name_length = len(name)
# 根据姓名的长度,重复相应的次数
for _ in range(name_length):
# 取出牌堆最上面的一张牌
top_card = cards.pop(0)
# 将这张牌放到牌堆的最下面
cards.append(top_card)
# 返回处理后的牌堆
return cards
# 定义一个函数,将牌堆最上的3张拿出,随机插入到牌堆中
def take_top_and_insert(self, cards):
# 取出牌堆最上的3张牌
top_three_cards = cards[:3]
# 获取剩余的牌
remaining_cards = cards[3:]
# 随机选择一个插入的位置
insert_index = random.randint(1, len(remaining_cards))
# 将3张牌插入到随机选择的位置
shuffled_cards = remaining_cards[:insert_index] + top_three_cards + remaining_cards[insert_index:]
# 返回处理后的牌堆
return shuffled_cards
# 定义一个函数,取出牌堆最上的一张牌
def take_top_card(self, cards):
# 取出牌堆最上的一张牌
top_card = cards.pop(0)
# 返回这张牌
return top_card
# 定义一个函数,根据地区插入牌
def insert_cards_based_on_region(self, cards, region):
# 如果地区是南方,插入1张牌
if region == "南":
insert_count = 1
# 如果地区是北方,插入2张牌
elif region == "北":
insert_count = 2
# 如果地区既不是南方也不是北方,插入3张牌
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
# 定义一个函数,根据性别取牌和念口诀
def take_and_chant(self, cards, gender, chant="见证奇迹的时刻"):
# 如果性别是男,取1张牌
if gender == "男":
take_count = 1
# 如果性别是女,取2张牌
elif gender == "女":
take_count = 2
# 如果性别既不是男也不是女,打印"未知性别"
else:
print("未知性别")
# 获取剩余的牌
remaining_cards = cards[take_count:]
# 根据口诀的长度,重复相应的次数
for c in chant:
# 取出牌堆最上面的一张牌
remaining_cards.append(remaining_cards.pop(0))
# 返回处理后的牌堆
return remaining_cards
# 定义一个函数,念口诀并修改牌堆
def chant_and_modify(self, cards):
# 初始化轮数
iter = 1
# 当牌堆中还有多于1张牌时,继续念口诀和修改牌堆
while len(cards) > 1:
# 定义口诀
stay_good_luck = "好运留下来"
bad_throw_away = "烦恼扔出去"
# 打印开始念口诀的轮数
print(f"\n第{iter}轮口诀开始:")
# 取出牌堆最上面的一张牌,放到牌堆的最下面
cards.append(cards.pop(0))
# 打印念完口诀后的牌堆
print(f"口诀{stay_good_luck}结束后手上的牌:", cards)
# 取出牌堆最上面的一张牌
cards.pop(0)
# 打印念完口诀后的牌堆
print(f"口诀{bad_throw_away}结束后手上的牌:", cards)
# 轮数加1
iter += 1
# 返回最后剩下的一张牌
return cards[0]
# 定义一个函数,开始游戏
def play(self):
# 将牌撕成两半后堆叠
split_cards = self.split_and_stack(self.selected_cards)
# 打印撕成两半后堆叠的牌
print("撕成两半后堆叠", split_cards)
# 重复姓名字数次后的牌堆
split_cards_repeated = self.repeat_name(split_cards, self.name)
# 打印重复姓名字数次后的牌堆
print(f"{self.name}重复姓名字数次后的牌堆:", split_cards_repeated)
# 将牌堆最上的3张拿出,随机插入到牌堆中
shuffled_cards = self.take_top_and_insert(split_cards_repeated)
# 打印处理后的牌堆
print("牌堆最上的3张拿出,随机插入后的牌堆:", shuffled_cards)
# 取出牌堆最上的一张牌
top_card = self.take_top_card(shuffled_cards)
# 打印取出的牌
print("拿走的牌:", top_card)
# 打印剩余的牌
print("剩余的牌:", shuffled_cards)
# 根据地区插入牌
shuffled_cards_region = self.insert_cards_based_on_region(shuffled_cards, self.region)
# 打印处理后的牌堆
print(f"{self.region}方地区插入后的牌堆:", shuffled_cards_region)
# 根据性别取牌和念口诀
remaining_cards = self.take_and_chant(shuffled_cards_region, self.gender, self.chant)
# 打印剩余的牌堆
print(f"剩余的牌堆:", remaining_cards)
# 念口诀并修改牌堆
final_card = self.chant_and_modify(remaining_cards)
# 打印最后剩下的一张牌
print(f"\n最终留下的牌{final_card},Step 4:{top_card}")
# 创建一个CardGame的实例
game = CardGame()
# 开始游戏
game.play()
大家可以自己去试一试,在步骤6后男生拿走的牌总是会在对应的第5位,女生拿走的牌总是会在对应的第3位。
实现结果如下:
随机抽取其中的四张牌: [('黑桃', '2'), ('方块', 'A'), ('红桃', '3'), ('方块', '9')]
请输入你的姓名:田中智
请输入你的性别(男/女):男
请输入你所在的地区(南/北):北
撕成两半后堆叠 [('黑桃', '2'), ('方块', 'A'), ('红桃', '3'), ('方块', '9'), ('黑桃', '2'), ('方块', 'A'), ('红桃', '3'), ('方块', '9')]
田中智重复姓名字数次后的牌堆: [('方块', '9'), ('黑桃', '2'), ('方块', 'A'), ('红桃', '3'), ('方块', '9'), ('黑桃', '2'), ('方块', 'A'), ('红桃', '3')]
牌堆最上的3张拿出,随机插入后的牌堆: [('红桃', '3'), ('方块', '9'), ('黑桃', '2'), ('方块', '9'), ('黑桃', '2'), ('方块', 'A'), ('方块', 'A'), ('红桃', '3')]
拿走的牌: ('红桃', '3')
剩余的牌: [('方块', '9'), ('黑桃', '2'), ('方块', '9'), ('黑桃', '2'), ('方块', 'A'), ('方块', 'A'), ('红桃', '3')]
北方地区插入后的牌堆: [('方块', '9'), ('黑桃', '2'), ('方块', '9'), ('黑桃', '2'), ('方块', 'A'), ('方块', 'A'), ('红桃', '3')]
剩余的牌堆: [('方块', '9'), ('黑桃', '2'), ('方块', 'A'), ('方块', 'A'), ('红桃', '3'), ('黑桃', '2')]
第1轮口诀开始:
口诀好运留下来结束后手上的牌: [('黑桃', '2'), ('方块', 'A'), ('方块', 'A'), ('红桃', '3'), ('黑桃', '2'), ('方块', '9')]
口诀烦恼扔出去结束后手上的牌: [('方块', 'A'), ('方块', 'A'), ('红桃', '3'), ('黑桃', '2'), ('方块', '9')]
第2轮口诀开始:
口诀好运留下来结束后手上的牌: [('方块', 'A'), ('红桃', '3'), ('黑桃', '2'), ('方块', '9'), ('方块', 'A')]
口诀烦恼扔出去结束后手上的牌: [('红桃', '3'), ('黑桃', '2'), ('方块', '9'), ('方块', 'A')]
第3轮口诀开始:
口诀好运留下来结束后手上的牌: [('黑桃', '2'), ('方块', '9'), ('方块', 'A'), ('红桃', '3')]
口诀烦恼扔出去结束后手上的牌: [('方块', '9'), ('方块', 'A'), ('红桃', '3')]
第4轮口诀开始:
口诀好运留下来结束后手上的牌: [('方块', 'A'), ('红桃', '3'), ('方块', '9')]
口诀烦恼扔出去结束后手上的牌: [('红桃', '3'), ('方块', '9')]
第5轮口诀开始:
口诀好运留下来结束后手上的牌: [('方块', '9'), ('红桃', '3')]
口诀烦恼扔出去结束后手上的牌: [('红桃', '3')]
最终留下的牌('红桃', '3'),Step 4:('红桃', '3')
看到这个,不知道小尼作何感想。。。。
创作不易,请大家点赞留言红包支持。如有问题,欢迎大家指出!