Python圣诞抽名脚本执行耗时不稳定问题排查与优化求助
圣诞节送礼配对脚本卡顿问题排查与解决
问题描述
我正在学习Python,写了个“从帽子里抽名字”的脚本用来做圣诞节送礼配对,脚本能正常运行,但执行时间极不稳定——有时候秒出结果,有时候卡5分钟都完不成。想搞清楚原因,找到能稳定运行的解决办法。
我的实现步骤如下:
- 创建两个相同名单并打乱:
fam1 = ["name1", "name2", "name3", "name4", "name5", "name6", "name7"] fam2 = ["name1", "name2", "name3", "name4", "name5", "name6", "name7"] fam1_shuffled = random.sample(fam1, len(fam1)) fam2_shuffled = random.sample(fam2, len(fam2))
- 定义禁止配对的字典:
not_allowed_pairs = { "name1": "name4", "name4": "name1", "name3": "name6", "name6": "name3" }
- 核心配对函数:
def pick_names(list1, list2): pairs = {} gifters = list1 used_names = [] while len(pairs) < len(gifters): for i in range(len(list1)): if ((gifters[i] != list2[i]) & (list2[i] not in used_names)): k = gifters[i] v = list2[i] if (k, v) not in non_allowed_pairs.items(): pairs[k] = v used_names.append(v) return pairs
- 结果输出函数:
def print_picks(pair_dict): for k, v in pair_dict.items(): print(f"{k} picked: {v}")
卡顿原因
你的核心逻辑存在无限循环风险,这是导致卡顿的根本原因:
- 循环逻辑缺陷:外层
while循环依赖pairs的长度增长,但内层for循环每次遍历只能匹配部分符合条件的项。如果剩下的未配对人员找不到合法的配对(比如最后一个人剩下的唯一可选对象是自己或禁止配对的人),pairs的长度就不会再增加,while循环会无限执行下去。 - 低效的条件判断:
list2[i] not in used_names是线性查找,虽然7个元素影响不大,但也是额外性能开销;另外你用了位运算符&代替逻辑运算符and,虽然结果没问题,但属于用法错误。 - 配对逻辑不严谨:简单从两个打乱的列表按索引匹配,没有处理“配对耗尽合法选项”的死局情况,随机打乱后的组合可能永远无法完成全部配对。
解决办法
正确思路是生成符合规则的合法排列——每个人抽到别人,且避开禁止配对,而非靠随机打乱硬凑。推荐两种可靠实现:
方法1:基于随机排列的重试机制
每次生成随机排列,检查是否符合规则,不符合就重新生成,直到找到合法结果:
import random fam = ["name1", "name2", "name3", "name4", "name5", "name6", "name7"] not_allowed_pairs = { "name1": "name4", "name4": "name1", "name3": "name6", "name6": "name3" } def generate_valid_pairs(): while True: # 生成随机排列,模拟抽名字的过程 recipients = random.sample(fam, len(fam)) valid = True # 检查两个核心规则:没人抽到自己,无禁止配对 for giver, receiver in zip(fam, recipients): if giver == receiver or not_allowed_pairs.get(giver) == receiver: valid = False break if valid: return dict(zip(fam, recipients)) def print_picks(pair_dict): for k, v in pair_dict.items(): print(f"{k} picked: {v}") # 生成并输出结果 pairs = generate_valid_pairs() print_picks(pairs)
对于7个人的规模,几乎不会多次重试,执行速度极快。
方法2:改进原逻辑,避免死循环
如果想保留列表配对思路,每次循环重新打乱列表,直到能生成完整合法配对:
import random fam = ["name1", "name2", "name3", "name4", "name5", "name6", "name7"] not_allowed_pairs = { "name1": "name4", "name4": "name1", "name3": "name6", "name6": "name3" } def pick_names(gifters): while True: recipients = random.sample(gifters, len(gifters)) pairs = {} used = set() valid = True for giver, receiver in zip(gifters, recipients): if giver == receiver or receiver in used or not_allowed_pairs.get(giver) == receiver: valid = False break pairs[giver] = receiver used.add(receiver) # 确认所有人都完成配对 if len(pairs) == len(gifters): return pairs def print_picks(pair_dict): for k, v in pair_dict.items(): print(f"{k} picked: {v}") fam_shuffled = random.sample(fam, len(fam)) pairs = pick_names(fam_shuffled) print_picks(pairs)
用set存储已使用的名字,查找速度比列表快;同时每次循环直接检查能否生成完整合法配对,不行就重新打乱,彻底避免了原逻辑的无限循环问题。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

