Python复现2024年春晚刘谦魔术

今年的央视春晚确实有点看头,要不是小尼的那个失误,这个节目就没有这么开心。

刘谦的这个魔术,我总觉得是个数学问题,就安奈不住自己,忍不住用编程去模拟一下这个过程。正好用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张被撕成两半的牌组成的牌组,就是对列表进行一个简单的操作了。(どろ モンスターカード)

Python复现2024年春晚刘谦魔术_第1张图片

抱歉走错片场了^_^

python实现对应的过程

0 确定这个程序需要引用3个模块

random(用于随机使用),itertools(用于最后的迭代使用),copy(用于复制牌组)

# 导入所需的库
import random
import itertools
import copy

1 定义这个魔术类为CardGame,并定义初始化函数:

生成一幅完整的扑克牌牌组。然后直接洗牌(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 = "见证奇迹的时刻"

2、在CardGame类中定义一个函数,实现step1

用copy方法,把已经获得的牌组模拟撕成两半,然后进行堆叠,获得新的牌组

# 定义一个函数,将牌撕成两半后堆叠
    def split_and_stack(self, cards):
        # 复制一份牌
        cards_copy = copy.copy(cards)
        # 将原牌和复制的牌堆叠在一起
        merged_cards = cards + cards_copy
        # 返回堆叠后的牌
        return merged_cards

后续定义的函数全是在CardGame中定义的

3、实现step2

首先就是要把姓名的长度转换成次数,然后利用循环机制进行重复。这里注意需要将堆在最上的牌放到最下面。

# 定义一个函数,重复姓名字数次后的牌堆
    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

4、函数实现step3

将牌堆最上的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

5、函数实现step4:

将牌堆最上方的牌拿走,放在一旁。这张牌就是要实现对比的那个。(就是坐在屁股下的那张)

 # 定义一个函数,取出牌堆最上的一张牌
    def take_top_card(self, cards):
        # 取出牌堆最上的一张牌
        top_card = cards.pop(0)
        # 返回这张牌
        return top_card

6、函数实现step5

按照南/北/不知道是南或者北方地区,判断自己属于哪一地区,并分别将牌堆最上的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

7、函数实现step6

按性别男/女,从牌堆最上方拿走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

8、函数实现step7

念口诀“好运留下来”时,将牌堆最上的牌放到牌堆最下;念“烦恼扔出去”时,将牌堆最上方的牌移除。重复这两句口诀,直到手中只有一张牌。

# 定义一个函数,念口诀并修改牌堆
    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')

看到这个,不知道小尼作何感想。。。。

创作不易,请大家点赞留言红包支持。如有问题,欢迎大家指出!

你可能感兴趣的:(我的python日常,算法,python)