查看全课大纲(第 36 / 51 节)
- 1.为什么人人都该学点编程?
- 2.计算机与程序设计:你的电脑到底在“想”什么?
- 3.Python 这门语言,到底有什么魔力?
- 4.Python 开发环境配置:如何拥有你的第一个“编程工具箱”?
- 5.如何安装一个更强大的代码编辑器?
- 6.汇率兑换1.0:你的第一个“钱生钱”小程序
- 7.你的第一行代码,藏着哪些秘密?
- 8.汇率兑换2.0(上):让程序看懂“CNY”和“USD”
- 9.汇率兑换程序,如何让它“聪明”地判断币种?
- 10.汇率兑换程序能一直运行,直到我喊停吗?
- 11.汇率兑换4.0:如何把“算钱”功能打包成积木?
- 12.汇率兑换5.0:给你的程序穿上“结构化”的西装
- 13.怎么用 Python 画一个会动的五角星?
- 14.如何让程序自动画出不同大小的五角星?
- 15.函数自己调用自己,会发生什么?
- 16.用递归画出神奇的分形树
- 17.Python 怎么计算你一天躺着不动能消耗多少热量?
- 18.如何让程序“活”起来,持续为你服务?
- 19.如何让 BMR 计算器更“聪明”?
- 20.当程序遇到“不按套路出牌”的用户怎么办?
- 21.怎么用 Python 模拟一个“滚雪球”式的存钱计划?
- 22.如何用列表记录每一周的存款?
- 23.如何让程序自己数数?—— 用 for 循环解放你的双手
- 24.52周存钱挑战4.0:你的存款计算器能有多灵活?
- 25.52周存钱挑战:怎么根据日期自动查存款?
- 26.Python 怎么判断一年中的第几天?
- 27.列表 vs 元组:哪个更适合存储月份天数?
- 28.用集合给月份分个队,换个思路算天数
- 29.把月份和天数“锁”在一起:用字典给数据配个对
- 30.Python 怎么判断密码强弱?
- 31.Python 怎么给密码设置“防沉迷”系统?
- 32.Python 怎么把密码保存到文件里?
- 33.Python 怎么读取文件里的密码?
- 34.Python 怎么把密码检查工具“打包”起来?
- 35.Python 怎么把文件操作也“打包”起来?
- 36.计算机怎么“扔骰子”?——用Python模拟随机事件
- 37.如何用 Python 模拟掷两个骰子?
- 38.怎么用 Python 把骰子轨迹画出来?
- 39.骰子点数分布长啥样?用直方图一眼看穿!
- 40.用科学计算库让骰子程序“起飞”
- 41.空气质量指数怎么算?手把手教你写一个AQI计算器
- 42.空气质量指数计算:从零开始,让程序看懂空气
- 43.如何读取空气质量数据文件并找出空气质量最好的五个监测点?
- 44.JSON 数据怎么变成 Excel 表格?—— 让数据换个“马甲”
- 45.Python 怎么让程序自己判断文件类型?
- 46.如何让程序自己上网查空气质量?
- 47.用美味的汤来解析网页:Beautiful Soup 入门
- 48.如何让爬虫自动抓取全国所有城市的空气质量?
- 49.爬到的数据怎么存下来?—— 把全国空气质量数据保存为CSV文件
- 50.用 pandas 玩转空气质量数据:找出全国空气最好和最差的城市
- 51.空气质量数据怎么“洗”干净,还能画成图?
计算机怎么“扔骰子”?——用Python模拟随机事件
约 21 分钟
小象实战讲义 · Python 入门:8 个小项目边玩边学
你有没有想过,计算机这个“死板”的家伙,怎么模拟现实生活中的随机事件?比如扔骰子、抽奖、或者游戏里的随机掉落?
今天我们就来玩一个有趣的项目:让计算机帮我们扔骰子!而且不是扔一次两次,是扔成千上万次,然后统计每个点数出现的次数和频率。你会发现,随着扔的次数越来越多,每个点数出现的频率会越来越接近1/6——这就是概率论里“大数定律”的直观体现!
学完这一节,你就能写出一个程序,让它帮你模拟扔骰子,看看“运气”到底是不是公平的。
💡 核心导读
- 🎲 项目目标:模拟扔一个骰子,统计各点数出现的次数和频率
- 🎯 本节新增:只学一个关键技能——用Python生成随机数
- 🔧 核心工具:
random模块(Python的“随机数生成器”) - 📊 数据统计:用列表记录结果,用循环统计频率
- 🔍 观察规律:扔的次数越多,结果越接近理论概率
一、骰子与随机数:计算机的“不确定性”
在现实生活中,扔一个正常的骰子,每次结果都是随机的——可能是1,可能是6,也可能是其他点数。但计算机程序是确定的,同一段代码每次运行结果都一样,怎么模拟这种“不确定性”呢?
答案就是:随机数生成器。
Python提供了一个非常方便的模块叫做random(英文就是“随机”的意思),它就像是一个虚拟的骰子,每次调用都能“摇”出一个随机数。
random模块的常用功能
让我们先来认识一下这个神奇的模块。在Python中,使用一个模块前需要先“引入”它:
# 引入random模块
import random引入之后,我们就可以使用random提供的各种函数了。下面是一些最常用的:
import random
random.seed(42) # 固定随机种子,让示例输出可复现
# 1. random():生成0到1之间的随机小数(包含0,不包含1)
print("0~1的随机小数:", random.random())
print("再运行一次:", random.random())
# 2. uniform(a, b):生成a到b之间的随机小数
print("10~100的随机小数:", random.uniform(10, 100))
# 3. randint(a, b):生成a到b之间的随机整数(包含a和b)
print("1~6的随机整数:", random.randint(1, 6))
print("再扔一次骰子:", random.randint(1, 6))
# 4. choice(list):从列表中随机选一个元素
colors = ['红', '黄', '蓝', '绿']
print("随机颜色:", random.choice(colors))
# 5. shuffle(list):打乱列表的顺序
numbers = [1, 2, 3, 4, 5, 6]
random.shuffle(numbers)
print("打乱后的列表:", numbers)运行上面的代码,你会得到下面这一组结果。注意代码第 2 行写了 random.seed(42):它把随机数“固定”在同一个起点,所以你每次运行都会看到完全一样的输出(另外 shuffle 还会直接改变原列表)。这是课程为了让大家对照而做的固定。
0~1的随机小数: 0.6394267984578837
再运行一次: 0.025010755222666936
10~100的随机小数: 34.75263865322073
1~6的随机整数: 2
再扔一次骰子: 2
随机颜色: 红
打乱后的列表: [4, 2, 3, 1, 5, 6]小提示:现在因为有
seed(42),你的结果会和上面一模一样。随机数的本性其实是“每次不同”——把random.seed(42)这一行删掉再运行,每次就都会得到不同的新结果了。
二、模拟扔一个骰子
现在我们知道怎么生成随机数了,接下来就正式模拟扔骰子。一个标准的骰子有6个面,点数从1到6,所以我们需要生成1到6之间的随机整数。
第一步:定义“扔骰子”的函数
为了让代码更清晰,我们先把“扔骰子”这个动作封装成一个函数:
import random
random.seed(42) # 固定随机种子,让示例输出可复现
def roll_dice():
"""
模拟扔骰子
返回1到6之间的随机整数
"""
roll = random.randint(1, 6)
return roll
# 测试一下
print("第一次扔:", roll_dice())
print("第二次扔:", roll_dice())
print("第三次扔:", roll_dice())运行结果:
第一次扔: 6
第二次扔: 1
第三次扔: 1看,我们的虚拟骰子已经能正常工作了!每次调用roll_dice()函数,就相当于扔了一次骰子。
第二步:扔多次并记录结果
只扔一次没什么意思,我们想看看扔很多次会是什么结果。比如扔10次、100次,甚至10000次。
我们需要一个“记录本”来记录每个点数出现的次数。Python的列表(list)非常适合这个任务:
import random
random.seed(42) # 固定随机种子,让示例输出可复现
def roll_dice():
"""模拟扔骰子"""
return random.randint(1, 6)
# 设置扔骰子的总次数
total_times = 10
# 初始化记录列表:6个0,分别对应点数1~6出现的次数
# [0, 0, 0, 0, 0, 0] 表示:1出现0次,2出现0次,...,6出现0次
result_list = [0] * 6
# 开始扔骰子!
for i in range(total_times):
# 扔一次骰子
roll = roll_dice()
# 根据点数更新记录
# 方法1:用if判断(直观但有点啰嗦)
if roll == 1:
result_list[0] += 1
elif roll == 2:
result_list[1] += 1
elif roll == 3:
result_list[2] += 1
elif roll == 4:
result_list[3] += 1
elif roll == 5:
result_list[4] += 1
elif roll == 6:
result_list[5] += 1
print("各点数出现的次数:", result_list)运行结果:
各点数出现的次数: [3, 3, 1, 0, 0, 3]这个结果表示:在10次投掷中,点数1出现了3次,点数2出现了3次,点数3出现了1次,点数4出现了0次,点数5出现了0次,点数6出现了3次。列表下标0到5依次对应点数1到6,次数加起来 3+3+1+0+0+3=10,正好等于投掷总次数。
第三步:优化统计方法
上面的代码用了6个if语句,虽然能工作,但有点重复。我们可以用循环来简化:
import random
random.seed(42) # 固定随机种子,让示例输出可复现
def roll_dice():
"""模拟扔骰子"""
return random.randint(1, 6)
total_times = 10
result_list = [0] * 6
for i in range(total_times):
roll = roll_dice()
# 用循环判断是哪个点数
# j从1到6,分别对应6个可能的点数
for j in range(1, 7): # range(1, 7)生成1,2,3,4,5,6
if roll == j:
# 注意:列表索引从0开始,所以点数j对应索引j-1
result_list[j - 1] += 1
break # 找到匹配的点数后就可以跳出内层循环了
print("各点数出现的次数:", result_list)各点数出现的次数: [3, 3, 1, 0, 0, 3]这样代码就更简洁了。不过换成 20 面骰时不止改一处:randint(1, 6) 的右端点要改成 20,统计列表的长度也要跟着变——result_list = [0] * 6 要改成 [0] * 20(否则第 7~20 点没格子计数,还会下标越界)。两处要配套修改,这是初学者最容易漏的地方。
三、显示详细的统计结果
现在我们有了每个点数出现的次数,但输出格式不够友好。我们想要这样的效果:
点数1的次数:2,频率:0.20
点数2的次数:1,频率:0.10
...同时获取索引和值:enumerate()函数
在遍历列表时,我们通常只能获取到元素的值。但如果同时需要索引号(位置)和值,该怎么办呢?
Python提供了一个很方便的函数:enumerate()(英文是”枚举”的意思)。看个例子:
# 普通遍历:只能获取值
fruits = ['苹果', '香蕉', '橙子']
for fruit in fruits:
print(fruit)
print("---")
# 使用enumerate:同时获取索引和值
for i, fruit in enumerate(fruits):
print(f"第{i}个水果是:{fruit}")运行结果:
苹果
香蕉
橙子
---
第0个水果是:苹果
第1个水果是:香蕉
第2个水果是:橙子enumerate()返回的是一个个元组(tuple),每个元组包含两个元素:索引和值。我们用i, fruit分别接收这两个值。
完善我们的统计输出
现在用enumerate()来改进骰子统计程序:
import random
random.seed(42) # 固定随机种子,让示例输出可复现
def roll_dice():
"""模拟扔骰子"""
return random.randint(1, 6)
total_times = 1000 # 这次扔1000次,看得更清楚
result_list = [0] * 6
# 扔骰子并统计
for i in range(total_times):
roll = roll_dice()
for j in range(1, 7):
if roll == j:
result_list[j - 1] += 1
break
# 用enumerate输出详细结果
print(f"扔骰子{total_times}次的结果:")
for i, count in enumerate(result_list):
# i是索引(0~5),对应点数i+1
# count是该点数出现的次数
frequency = count / total_times # 计算频率
print(f"点数{i+1}的次数:{count},频率:{frequency:.3f}") # :.3f表示保留3位小数运行结果:
扔骰子1000次的结果:
点数1的次数:166,频率:0.166
点数2的次数:172,频率:0.172
点数3的次数:162,频率:0.162
点数4的次数:165,频率:0.165
点数5的次数:160,频率:0.160
点数6的次数:175,频率:0.175注意:代码固定了
random.seed(42),所以你的结果会和上面完全一样;删掉这一行再运行,每次才会不同,但无论怎么变,各点数的频率都在 0.166 左右。
四、观察概率的魔力
让我们做一个有趣的实验:分别扔10次、100次、1000次、10000次,看看频率有什么变化。
import random
random.seed(42) # 固定随机种子,让示例输出可复现
def roll_dice():
"""模拟扔骰子"""
return random.randint(1, 6)
def simulate_dice_rolls(times):
"""模拟扔指定次数的骰子并返回结果"""
result_list = [0] * 6
for i in range(times):
roll = roll_dice()
for j in range(1, 7):
if roll == j:
result_list[j - 1] += 1
break
return result_list
# 测试不同次数
test_times = [10, 100, 1000, 10000]
for total_times in test_times:
print(f"\n扔骰子{total_times}次:")
results = simulate_dice_rolls(total_times)
for i, count in enumerate(results):
frequency = count / total_times
print(f" 点数{i+1}: {count}次 ({frequency:.3f})")运行结果示例:
扔骰子10次:
点数1: 3次 (0.300)
点数2: 3次 (0.300)
点数3: 1次 (0.100)
点数4: 0次 (0.000)
点数5: 0次 (0.000)
点数6: 3次 (0.300)
扔骰子100次:
点数1: 19次 (0.190)
点数2: 17次 (0.170)
点数3: 18次 (0.180)
点数4: 11次 (0.110)
点数5: 15次 (0.150)
点数6: 20次 (0.200)
扔骰子1000次:
点数1: 161次 (0.161)
点数2: 169次 (0.169)
点数3: 159次 (0.159)
点数4: 173次 (0.173)
点数5: 169次 (0.169)
点数6: 169次 (0.169)
扔骰子10000次:
点数1: 1636次 (0.164)
点数2: 1598次 (0.160)
点数3: 1637次 (0.164)
点数4: 1702次 (0.170)
点数5: 1671次 (0.167)
点数6: 1756次 (0.176)发现规律了吗?
- 扔10次时,频率波动很大(这一组里从 0 到 0.3 都有)
- 扔100次时,波动变小了
- 扔1000次时,基本都在0.166左右
- 扔10000次时,非常接近理论值1/6 ≈ 0.1667
这就是大数定律的直观体现:随着试验次数增加,频率会越来越接近理论概率。对于一个均匀的骰子,每个点数出现的概率应该是1/6 ≈ 16.67%。
五、完整程序与固定种子
为了让你的运行结果和讲义一致(方便对照),我们可以给随机数生成器设置一个“固定种子”。这样每次运行都会产生相同的随机序列。
import random
# 设置随机种子,让每次运行结果一致
random.seed(42) # 42是个常用的魔法数字,你可以改成任何整数
def roll_dice():
"""模拟扔骰子"""
return random.randint(1, 6)
def main():
"""主函数"""
total_times = 10000
# 初始化记录列表
result_list = [0] * 6
# 扔骰子并统计
for i in range(total_times):
roll = roll_dice()
for j in range(1, 7):
if roll == j:
result_list[j - 1] += 1
break
# 输出结果
print(f"扔骰子{total_times}次的结果:")
for i, count in enumerate(result_list):
frequency = count / total_times
print(f"点数{i+1}的次数:{count},频率:{frequency:.4f}")
# 理论值
print(f"\n理论频率:{1/6:.4f}")
# 运行程序
main()运行结果(固定种子为42时):
扔骰子10000次的结果:
点数1的次数:1618,频率:0.1618
点数2的次数:1629,频率:0.1629
点数3的次数:1647,频率:0.1647
点数4的次数:1687,频率:0.1687
点数5的次数:1671,频率:0.1671
点数6的次数:1748,频率:0.1748
理论频率:0.1667实际应用时:去掉
random.seed(42)这行,这样每次运行都会得到真正的随机结果。
📝 动手练一练
修改骰子面数:如果骰子不是6面,而是20面(点数1~20),该怎么修改程序?试试看!
统计最大值:在扔完骰子后,找出出现次数最多的点数和最少的点数。
验证均匀性:计算所有点数的频率与理论频率(1/面数)的绝对误差之和,这个值越小说明骰子越均匀。
👉 点击查看参考答案
1. 20面骰子的修改思路:
import random
random.seed(42) # 固定随机种子,让示例输出可复现
def roll_dice(sides=6):
"""模拟扔骰子,默认6面"""
return random.randint(1, sides)
total_times = 1000
sides = 20 # 20面骰子
result_list = [0] * sides # 创建20个元素的列表
for i in range(total_times):
roll = roll_dice(sides)
result_list[roll - 1] += 1 # 直接使用点数作为索引
for i, count in enumerate(result_list):
frequency = count / total_times
print(f"点数{i+1}的次数:{count},频率:{frequency:.3f}")点数1的次数:45,频率:0.045
点数2的次数:46,频率:0.046
点数3的次数:57,频率:0.057
点数4的次数:51,频率:0.051
点数5的次数:43,频率:0.043
点数6的次数:42,频率:0.042
点数7的次数:49,频率:0.049
点数8的次数:67,频率:0.067
点数9的次数:64,频率:0.064
点数10的次数:49,频率:0.049
点数11的次数:48,频率:0.048
点数12的次数:34,频率:0.034
点数13的次数:44,频率:0.044
点数14的次数:58,频率:0.058
点数15的次数:56,频率:0.056
点数16的次数:49,频率:0.049
点数17的次数:40,频率:0.040
点数18的次数:63,频率:0.063
点数19的次数:48,频率:0.048
点数20的次数:47,频率:0.0472. 统计最常出现和最不常出现的点数:
import random
# 模拟数据(实际使用时用真实模拟结果)
result_list = [168, 158, 176, 170, 162, 166] # 1000次模拟的结果
max_count = max(result_list)
min_count = min(result_list)
max_point = result_list.index(max_count) + 1
min_point = result_list.index(min_count) + 1
print(f"最常出现的点数:{max_point},出现了{max_count}次")
print(f"最不常出现的点数:{min_point},出现了{min_count}次")最常出现的点数:3,出现了176次
最不常出现的点数:2,出现了158次3. 验证均匀性:
import random
random.seed(42) # 固定随机种子,让示例输出可复现
total_times = 10000
sides = 6
result_list = [0] * sides
# 模拟扔骰子(简化版)
for i in range(total_times):
roll = random.randint(1, sides)
result_list[roll - 1] += 1
# 计算总误差
theory_freq = 1 / sides
total_error = 0
for count in result_list:
actual_freq = count / total_times
error = abs(actual_freq - theory_freq) # 绝对误差
total_error += error
print(f"理论频率:{theory_freq:.4f}")
print(f"总绝对误差:{total_error:.4f}")
print(f"平均每个点数的误差:{total_error/sides:.4f}")理论频率:0.1667
总绝对误差:0.0212
平均每个点数的误差:0.0035本章小结
今天我们完成了一个很有趣的项目:用计算机模拟扔骰子。通过这个项目,你学到了:
- 随机数的生成:使用
random模块生成各种随机数 - 函数的封装:把”扔骰子”这个功能封装成
roll_dice()函数 - 列表的统计:用列表记录每个点数出现的次数
- enumerate的使用:在遍历时同时获取索引和值
- 概率的观察:随着试验次数增加,频率趋近理论概率
最重要的是,我们只引入了一个新知识点:random模块的使用。其他都是之前项目中学过的内容(函数、列表、循环、条件判断)的巩固和组合。
📋 行动清单
学完这一节,你可以:
- 运行完整程序,观察扔10000次骰子的结果
- 修改骰子面数,试试20面骰子或100面骰子
- 思考应用场景:随机数还能用在哪些地方?(抽奖、游戏、随机密码…)
- 挑战自己:如果不使用
enumerate(),你能用其他方法输出带点数的统计结果吗?
下一节,我们将升级这个项目:同时扔两个骰子!你会发现,两个骰子的点数之和的分布不再是均匀的,而是中间高、两边低的”钟形”分布。这又是为什么呢?我们下节揭晓!
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载第8章代码包:模拟掷骰子(含全部版本 5 个 .py)ZIP · 3KB