如何从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张给堆1,第2张堆2,第3张堆3,循环发完)。
- 重复步骤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
相关产品推荐
相关产品推荐

