You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从21个唯一值列表生成3个无重复子列表?Python纸牌魔术求助

解决21张牌分3堆无重复问题&纸牌魔术推进方案

嘿,我来帮你搞定这个问题!首先看你代码里的核心问题:你现在每次循环都打印deck的前7张,所以三堆完全一模一样,这肯定不是你要的效果。我们先解决分堆无重复的问题,再聊聊后续纸牌魔术怎么推进。

一、快速实现无重复分堆

你的21张牌都是唯一值,洗牌后只要把列表拆成3个各7元素的子列表就行,每堆自然不会有重复(因为原列表本身没有重复元素,拆分后也不会出现重复)。

修正后的基础分堆代码:

import random

# 初始化21张唯一牌的牌堆
deck = ["S A","S 2","S 3","S 4","S 5","S 6","S 7",
        "D A","D 2","D 3","D 4","D 5","D 6","D 7",
        "C A","C 2","C 3","C 4","C 5","C 6","C 7"]

# 打乱牌堆
random.shuffle(deck)

# 将打乱后的牌堆拆分为3个各7张的子堆
piles = [deck[i*7 : (i+1)*7] for i in range(3)]

# 打印每一堆,清晰分隔
for pile_num, pile in enumerate(piles, start=1):
    print(f"Pile {pile_num}:")
    for card in pile:
        print(card)
    print()  # 空行分隔不同堆

代码说明

  • random.shuffle(deck):先把牌堆彻底打乱,保证随机性。
  • 列表推导式[deck[i*7 : (i+1)*7] for i in range(3)]:直接把打乱后的牌堆切成3段,每段7张,分别对应第1、2、3堆,完全避免重复。
  • enumerate(piles, start=1):给堆从1开始编号,比手动计算z+1更直观好读。

二、纸牌魔术的后续推进方案

常见的21张牌魔术核心是利用数学规律定位观众选的牌,给你一个简单易实现的经典流程,你可以直接复用或调整:

经典魔术流程逻辑

  1. 第一次分堆后,让观众选一张牌记住,放回原堆并告诉你这张牌在第几堆。
  2. 收牌时把观众选的那堆放在另外两堆的中间(关键操作)。
  3. 重新按顺序发牌分堆(第1张给堆1,第2张堆2,第3张堆3,循环发完)。
  4. 重复步骤1-3共3次,最后第11张牌就是观众选的那张(从1数的话,对应列表索引10)。

完整魔术代码实现

import random

def create_21card_deck():
    """创建21张牌的标准牌堆"""
    return ["S A","S 2","S 3","S 4","S 5","S 6","S 7",
            "D A","D 2","D 3","D 4","D 5","D 6","D 7",
            "C A","C 2","C 3","C 4","C 5","C 6","C 7"]

def deal_piles(deck):
    """按顺序发牌分堆:第1张堆1,第2张堆2,第3张堆3,循环"""
    piles = [[], [], []]
    for idx, card in enumerate(deck):
        piles[idx % 3].append(card)
    return piles

def print_piles(piles):
    """格式化打印牌堆"""
    for pile_num, pile in enumerate(piles, start=1):
        print(f"Pile {pile_num}:")
        for card in pile:
            print(card)
        print()

# 初始化并打乱牌堆
deck = create_21card_deck()
random.shuffle(deck)

# 第一次分堆
piles = deal_piles(deck)
print("=== 第一次分堆 ===")
print("请选一张牌,记住它所在的堆编号(1/2/3):")
print_piles(piles)

# 重复3次收牌-分堆流程
for round_num in range(3):
    # 获取观众选中的堆编号(转成0索引方便操作)
    selected_pile_idx = int(input(f"第{round_num+1}次:请输入你选的牌所在的堆编号:")) - 1
    
    # 核心操作:把选中的堆放在中间,重新组合牌堆
    if selected_pile_idx == 0:
        new_deck = piles[1] + piles[0] + piles[2]
    elif selected_pile_idx == 1:
        new_deck = piles[0] + piles[1] + piles[2]
    else:
        new_deck = piles[0] + piles[2] + piles[1]
    
    # 重新分堆并展示
    piles = deal_piles(new_deck)
    print(f"\n=== 第{round_num+2}次分堆 ===")
    print("请再次确认你选的牌所在的堆编号:")
    print_piles(piles)

# 揭晓答案:第11张牌就是观众选的(索引10)
print(f"\n你选的牌是:**{new_deck[10]}**")

额外小技巧

  • 如果不想用固定位置揭晓,最后一次分堆后可以让观众说堆编号,直接取该堆的第4张(中间位置),也是经典玩法。
  • 可以给打印内容加一些装饰性符号,让魔术的仪式感更强~

内容的提问来源于stack exchange,提问作者Randex Grandma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:19:48