用Python随机模拟一万次,春晚刘谦魔术证明堆栈方法有效
2024春晚刘谦魔术
本项目包含两个版本,使用堆栈方法,语言为python
证明版本
证明步骤如下:
1. 随机四张牌,假设为ABCD
2. 撕开它们并且同方向放置,手里的牌目前为ABCDABCD
3. 名字字数:随机一个数字n(2-7范围内的任意整数),将最前面的n张牌放到最后,例如n等于2,那么牌就是CDABCDAB
4. 将前三张牌取出并插入剩余牌的任意位置,例如第三步结束是CDABCDAB,将CDA随机插入剩下BCDAB里的任意位置,例如DA之间,即BCDCDAAB
5. 将最上面的牌取出,并放到一边。例如第四部结束为BCDCDAAB,那么只保留CDCDAAB,将B删除并放在一边
6. 南方人/北方人:从1-3中随机选取任意整数a,将剩余牌中的前a张插入剩余牌堆的任意位置。例如a=3,那么将前3张牌,也就是CDCDAAB中的CDC随机插入剩下的DAAB中任意位置,例如DA之间,那么就是DCDCAAB
7. 性别:从1-2中随机选取任意整数b,移去牌堆中的前b张牌。例如b=2,也就是从DCDCAAB中移去DC,目前剩余DCAAB
8. 见证奇迹的时刻:将第一张牌移到最后,重复7次,例如第七步剩余DCAAB,那么移动7次分别是CAABD,AABDC,ABDCA,BDCAA,DCAAB,CAABD,AABDC,最后得到AABDC
9. 好运留下来,烦恼丢出去:将第8步剩下的牌交替进行“将第一张牌移到最后”和“将第一张牌删除”这两个操作,直到只剩下一张牌。例如剩下的牌是AABDC,根据指令剩余的依次是ABDCA,BDCA,DCAB,CAB,ABC,BC,CB,最后扔掉C,剩余的牌B和第五步中放到一边的B相同
随机模拟一万次,正确率为100%
import random
def magic_proof():
# 初始牌组
cards = ['A', 'B', 'C', 'D'] * 2
# 1. 根据名字有几个字(随机选择2-7之间的整数),将前n张牌移到最后
n = random.randint(2, 7)
cards = cards[n:] + cards[:n]
# 2. 取出前三张牌并随机插入剩余牌中,不能插在第一张和最后一张
first_three = cards[:3]
cards = cards[3:]
for card in first_three:
insert_position = random.randint(1, len(cards) - 2)
cards.insert(insert_position, card)
# 3. 把最上面的牌放到一边
remembered_card = cards.pop(0)
# 4. 从最上面取牌,南方人取1张,北方人取2张,无法确定取3张,将这些牌随机插入剩下的牌中
a = random.randint(1, 3)
first_a = cards[:a]
cards = cards[a:]
for card in first_a:
insert_position = random.randint(1, len(cards) - 2)
cards.insert(insert_position, card)
# 5. 从最上面取牌,男生取1张,女生取2张,将这些牌扔掉
b = random.randint(1, 2)
cards = cards[b:]
# 6. 见证奇迹的时刻:每次将第一张牌移到最后,重复7次
for _ in range(7):
cards.append(cards.pop(0))
# 7. 好运留下来,烦恼丢出去:交替进行将第一张牌移到最后和删除,直到剩一张牌
while len(cards) > 1:
cards.append(cards.pop(0)) # 第一张牌移到最后
cards.pop(0) # 删除现在的第一张牌
# 返回最后剩下的牌和之前放在一边的牌
return cards[0], remembered_card
# 重新进行多次模拟并计算匹配的频率
correct_match_nb = 0
for _ in range(10000):
final_card, remembered_card = magic_proof()
if final_card == remembered_card:
correct_match_nb += 1
match_rate = correct_match_nb / 10000
print(match_rate)
互动版本
用户可自行输入参数
from collections import deque
import random
def get_input(prompt_message, expected_type=int, min_val=None, max_val=None):
while True:
user_input = input(prompt_message)
try:
value = expected_type(user_input)
if (min_val is not None and value < min_val) or (max_val is not None and value > max_val):
print(f"Error: Please enter a value between {min_val} and {max_val}.")
else:
return value
except ValueError:
print(f"输入类型或范围错误,请重新输入。")
# 获取名字字数,地域,性别
name = get_input("请输入名字字数,范围2-7:", int, 2, 7)
location = get_input("南方人请输入1,北方人请输入2,不确定请输入3:", int, 1, 3)
sexe = get_input("男生请输入1,女生请输入2:", int, 1, 2)
def simulate_magic(name, location, sexe):
# 洗牌
cards = ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]
random.shuffle(cards)
# 随机选择四张,对折撕开
selected_values = random.sample(cards, 4)
selceted_cards = deque(selected_values * 2)
print(f"抽出的牌:{selceted_cards}")
# 1. 根据名字有几个字,将前n张牌移到最后
selceted_cards.rotate(-name)
# 2. 取出前三张牌并随机插入剩余牌中,不能插在第一张和最后一张
first_three = [selceted_cards.popleft() for _ in range(3)]
for card in first_three:
insert_position = random.randint(1, len(selceted_cards) - 2)
selceted_cards.insert(insert_position, card)
# 3. 把最上面的牌放到一边
remembered_card = selceted_cards.popleft()
# 4. 南方人取1张,北方人取2张,无法确定取3张,将这些牌随机插入剩下的牌中
first_a = [selceted_cards.popleft() for _ in range(location)]
for card in first_a:
if len(selceted_cards) > 2:
insert_position = random.randint(1, len(selceted_cards) - 2)
else:
insert_position = 0
selceted_cards.insert(insert_position, card)
# 5. 男生取1张,女生取2张,将这些牌扔掉
for _ in range(sexe):
selceted_cards.popleft()
# 6. 见证奇迹的时刻
selceted_cards.rotate(-7)
# 7. 好运留下来,烦恼丢出去!
while len(selceted_cards) > 1:
selceted_cards.append(selceted_cards.popleft()) # 第一张牌移到最后
selceted_cards.popleft() # 删除现在的第一张牌
# 返回手里剩下的牌和放在一边的牌
return selceted_cards[0], remembered_card
final_card, remebered_card = simulate_magic(name, location, sexe)
print(f"初始牌:{remebered_card}, 剩下的牌:{final_card}")
print(f"初始牌和手里的牌是否相同:{remebered_card == final_card}")
print()
print("祝大家龙年大吉!")