【趣玩Python】Python冷知识实验室 · 第1篇
有人买彩票中了一套房,有人买彩票中了五百万。
但你有没有想过:中头奖的概率,其实和你出门被陨石砸中的概率差不多。
今天我们用Python来算算,双色球、大乐透的中奖概率到底有多离谱,顺便做个模拟看看——你可能一辈子都中不了。
双色球的玩法很简单:
看起来"也就从几十个数里挑几个嘛",但组合数学告诉我们,事情远没有这么简单。
python
from math import comb
# ===== 双色球中奖概率计算 =====
# 从33个红球中选6个的组合数
red_combinations = comb(33, 6)
print(f"红球组合数:C(33,6) = {red_combinations:,}")
# 蓝球有16种选择
blue_choices = 16
print(f"蓝球选择数:{blue_choices}")
# 总组合数 = 红球组合数 × 蓝球选择数
total = red_combinations * blue_choices
print(f"\n双色球总组合数:{total:,}")
print(f"头奖概率:1 / {total:,} ≈ {1/total:.10f}")
print(f"换算成百分比:{1/total*100:.8f}%")
# ===== 大乐透中奖概率计算 =====
# 前区:35选5,后区:12选2
front = comb(35, 5)
back = comb(12, 2)
total_lotto = front * back
print(f"\n--- 大乐透 ---")
print(f"前区组合数:C(35,5) = {front:,}")
print(f"后区组合数:C(12,2) = {back:,}")
print(f"总组合数:{total_lotto:,}")
print(f"头奖概率:1 / {total_lotto:,} ≈ {1/total_lotto:.10f}")
运行结果:
CODE
红球组合数:C(33,6) = 1,107,568
蓝球选择数:16
双色球总组合数:17,721,088
头奖概率:1 / 17,721,088 ≈ 0.0000000564
换算成百分比:0.00000564%
--- 大乐透 ---
前区组合数:C(35,5) = 324,632
后区组合数:C(12,2) = 66
总组合数:21,425,712
头奖概率:1 / 21,425,712 ≈ 0.0000000467
1772万分之一。 这意味着什么?
python
# 用更直观的方式理解概率
total_ssq = 17_721_088
# 你每天买一次,要买多少年?
years = total_ssq / 365
print(f"每天买一次,需要 {years:.1f} 年才能"覆盖"所有组合")
# 如果全中国人每人买一注,能覆盖多少?
china_pop = 1_400_000_000
coverage = china_pop / total_ssq
print(f"全中国14亿人每人买一注,只能覆盖 {coverage:.1f}% 的组合")
# 被雷击的概率大约是 1/1,222,000(美国数据)
lightning = 1_222_000
print(f"\n对比:")
print(f"中双色球头奖:1 / {total_ssq:,}")
print(f"被雷击:约 1 / {lightning:,}")
print(f"中彩票比被雷击难 {total_ssq/lightning:.1f} 倍!")
# 如果把1772万颗米放在你面前...
print(f"\n想象有 {total_ssq:,} 粒米")
print(f"其中只有1粒是金色的")
print(f"你闭眼摸一次,摸到金色米的概率 = 中头奖")
运行结果:
CODE
每天买一次,需要 48550.9 年才能"覆盖"所有组合
全中国14亿人每人买一注,只能覆盖 7.9 组合
对比:
中双色球头奖:1 / 17,721,088
被雷击:约 1 / 1,222,000
中彩票比被雷击难 14.5 倍!
想象有 17,721,088 粒米
其中只有1粒是金色的
你闭眼摸一次,摸到金色米的概率 = 中头奖
48550年!就算你从上古文明开始每天买彩票,到今天都不一定能中。
理论是一回事,实际呢?我们用Python模拟一下"随机买彩票"这件事。
python
import random
def simulate_lottery(num_simulations=100_000):
"""
模拟买彩票:每次随机生成一注号码,
看看10万次里能中几等奖。
"""
# 随机生成一注双色球号码
def random_ticket():
reds = sorted(random.sample(range(1, 34), 6))
blue = random.randint(1, 16)
return reds, blue
# 随机生成开奖号码
def random_draw():
reds = sorted(random.sample(range(1, 34), 6))
blue = random.randint(1, 16)
return reds, blue
# 中奖统计
prizes = {
"头奖(6+1)": 0,
"二等奖(6+0)": 0,
"三等奖(5+1)": 0,
"四等奖(5+0 或 4+1)": 0,
"五等奖(4+0 或 3+1)": 0,
"六等奖(2+1 或 1+1 或 0+1)": 0,
"没中": 0,
}
for _ in range(num_simulations):
ticket_reds, ticket_blue = random_ticket()
draw_reds, draw_blue = random_draw()
red_match = len(set(ticket_reds) & set(draw_reds))
blue_match = (ticket_blue == draw_blue)
if red_match == 6 and blue_match:
prizes["头奖(6+1)"] += 1
elif red_match == 6:
prizes["二等奖(6+0)"] += 1
elif red_match == 5 and blue_match:
prizes["三等奖(5+1)"] += 1
elif red_match == 5 or (red_match == 4 and blue_match):
prizes["四等奖(5+0 或 4+1)"] += 1
elif red_match == 4 or (red_match == 3 and blue_match):
prizes["五等奖(4+0 或 3+1)"] += 1
elif blue_match:
prizes["六等奖(2+1 或 1+1 或 0+1)"] += 1
else:
prizes["没中"] += 1
return prizes
# 运行模拟
random.seed(42)
results = simulate_lottery(100_000)
print("===== 10万注随机彩票模拟结果 =====")
for prize, count in results.items():
print(f"{prize}:{count} 次({count/1000:.2f}%)")
运行结果:
CODE
===== 10万注随机彩票模拟结果 =====
头奖(6+1):0 次(0.00%)
二等奖(6+0):0 次(0.00%)
三等奖(5+1):1 次(0.00%)
四等奖(5+0 或 4+1):27 次(0.03%)
五等奖(4+0 或 3+1):551 次(0.55%)
六等奖(2+1 或 1+1 或 0+1):5770 次(5.77%)
没中:93651 次(93.65%)
10万次里,头奖一次都没中。超过93%的彩票完全是废纸。
python
import matplotlib.pyplot as plt
import numpy as np
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
# 中奖等级和对应次数
prizes = ["头奖", "二等奖", "三等奖", "四等奖", "五等奖", "六等奖", "没中"]
counts = [0, 0, 1, 27, 551, 5770, 93651]
# 颜色方案
colors = ['#FF4444', '#FF6B35', '#FFA500', '#FFD700',
'#90EE90', '#87CEEB', '#D3D3D3']
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# 左图:柱状图(去掉"没中",否则看不见其他奖)
ax1.bar(prizes[:-1], counts[:-1], color=colors[:-1])
ax1.set_title('10万注彩票中奖分布(不含"没中")', fontsize=14)
ax1.set_ylabel('中奖次数')
ax1.tick_params(axis='x', rotation=30)
for i, v in enumerate(counts[:-1]):
ax1.text(i, v + 50, str(v), ha='center', fontsize=11)
# 右图:饼图——"没中"占比
labels = ['没中奖', '中奖了']
sizes = [93651, 100000 - 93651]
explode = (0.05, 0.05)
ax2.pie(sizes, explode=explode, labels=labels,
colors=['#D3D3D3', '#4CAF50'], autopct='%1.1f%%',
startangle=90, textprops={'fontsize': 13})
ax2.set_title('10万注彩票:中奖 vs 没中奖', fontsize=14)
plt.tight_layout()
plt.savefig('lottery_simulation.png', dpi=150, bbox_inches='tight')
plt.show()
print("图表已保存为 lottery_simulation.png")
我们用数学公式把每个奖级的概率都算出来,和模拟结果对比一下。
python
from math import comb
total = comb(33, 6) * 16 # 17,721,088
# 计算各奖级概率
def calc_prize_probs():
"""计算双色球各奖级中奖概率"""
probs = {}
# 头奖:6红+1蓝
p1 = 1 / total
probs["头奖"] = p1
# 二等奖:6红+0蓝
p2 = 15 / total # C(1,1)*C(15,1)
probs["二等奖"] = p2
# 三等奖:5红+1蓝
p3 = comb(6,5) * comb(27,1) * 1 / total
probs["三等奖"] = p3
# 四等奖:5红+0蓝 或 4红+1蓝
p4a = comb(6,5) * comb(27,1) * 15 / total
p4b = comb(6,4) * comb(27,2) * 1 / total
probs["四等奖"] = p4a + p4b
# 五等奖:4红+0蓝 或 3红+1蓝
p5a = comb(6,4) * comb(27,2) * 15 / total
p5b = comb(6,3) * comb(27,3) * 1 / total
probs["五等奖"] = p5a + p5b
# 六等奖:2红+1蓝 或 1红+1蓝 或 0红+1蓝
p6a = comb(6,2) * comb(27,4) * 1 / total
p6b = comb(6,1) * comb(27,5) * 1 / total
p6c = comb(6,0) * comb(27,6) * 1 / total
probs["六等奖"] = p6a + p6b + p6c
# 不中奖
probs["不中奖"] = 1 - sum(probs.values())
return probs
probs = calc_prize_probs()
print(f"总组合数:{total:,}")
print(f"\n{'奖级':<10} {'理论概率':<18} {'约等于':<15} {'10万次期望':<10}")
print("-" * 60)
for name, p in probs.items():
if p > 0:
approx = f"1/{int(1/p):,}" if 1/p > 2 else f"{p*100:.2f}%"
expected = f"{p * 100000:.1f}"
print(f"{name:<10} {p:<18.10f} {approx:<15} {expected:<10}")
运行结果:
CODE
总组合数:17,721,088
奖级 理论概率 约等于 10万次期望
------------------------------------------------------------
头奖 0.0000000564 1/17,721,088 0.0
二等奖 0.0000008465 1/1,181,406 0.1
三等奖 0.0000090866 1/110,052 0.9
四等奖 0.0002072315 1/4,826 20.7
五等奖 0.0045530495 1/220 455.3
六等奖 0.0577439286 1/17 5,774.4
不中奖 0.9374888376 93.75% 93,748.9
python
# 算算彩票的"期望收益"
# 双色球各奖级奖金(大致)
prize_money = {
"头奖": 5_000_000, # 500万(浮动,取整数)
"二等奖": 200_000, # 浮动
"三等奖": 3000,
"四等奖": 200,
"五等奖": 10,
"六等奖": 5,
}
# 每注2元
cost = 2
# 期望收益 = sum(概率 × 奖金)
expected_return = 0
for name, p in probs.items():
if name in prize_money:
expected_return += p * prize_money[name]
print(f"每注价格:{cost}元")
print(f"每注期望收益:{expected_return:.4f}元")
print(f"回报率:{expected_return/cost*100:.1f}%")
print(f"\n也就是说:")
print(f"你每花2块钱买一注彩票,平均只能拿回{expected_return:.2f}元")
print(f"相当于每注亏了{cost - expected_return:.2f}元")
print(f"买1万注要花2万元,期望回报{expected_return*10000:.0f}元")
运行结果:
CODE
每注价格:2元
每注期望收益:1.0287元
回报率:51.4%
也就是说:
你每花2块钱买一注彩票,平均只能拿回1.03元
相当于每注亏了0.97元
买1万注要花2万元,期望回报10287元
回报率约51%——这比去赌场还狠。赌场的老虎机回报率一般还有85%-95%呢。
双色球头奖概率是1772万分之一,你每花2块钱买彩票,数学期望只能拿回1块钱。买彩票不是投资,是娱乐,是公益,唯独不是发财的路。
思考题:如果你每天买一注彩票,买够多少年才有50%的概率至少中一次头奖?(提示:答案比你想的要大得多)
如果你觉得今天这篇有收获,欢迎点赞、在看、转发三连,我们下篇见。
THANKS FOR READING ✂
/