查看全课大纲(第 37 / 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 模拟掷两个骰子?
约 10 分钟
小象实战讲义 · Python 入门:8 个小项目边玩边学
上一节我们学会了用 Python 模拟掷一个骰子,并统计了每个点数出现的次数。但玩过飞行棋或大富翁的朋友都知道,很多游戏需要掷两个骰子。这一节,我们就来升级你的程序,让它能同时模拟两个骰子,并看看两个骰子的点数之和有什么有趣的规律!
💡 核心导读
- 项目升级:从模拟一个骰子,升级到模拟两个骰子。
- 新知识点:认识
zip()函数,它像拉链一样,能把两个列表的元素两两配成一对对键值对,再交给dict()就能拼成一个字典。 - 数据结构:当简单的列表无法直接对应结果时,我们请出更灵活的“字典”来帮忙。
- 有趣发现:两个骰子的点数之和,哪个数字最容易出现?哪个最难?程序会告诉你答案。
从1个到2个:问题升级了
在1.0版本中,我们用一个长度为6的列表,就轻松记录了一个骰子(点数1-6)的出现次数。因为列表的索引(0, 1, 2, 3, 4, 5)正好比点数(1, 2, 3, 4, 5, 6)少1,对应关系很简单。
但现在要掷两个骰子,我们关心的是两个骰子的点数之和。这个和最小是1+1=2,最大是6+6=12。结果范围变成了2到12,一共11种可能。
问题来了:我们还能用一个简单的列表来记录吗?列表的索引是从0开始的,怎么让它对应到2到12的点数和呢?有点麻烦,对不对?
别担心,Python 为我们准备了一个更趁手的工具:字典(Dictionary)。
认识新朋友:字典与 zip() 函数
字典就像一本真正的字典,每个“词条”(键,key)对应一个“解释”(值,value)。在我们的项目里,“词条”就是点数之和(2, 3, 4, …, 12),“解释”就是它出现的次数。
我们可以这样初始化一个字典:
# 笨办法:一个一个写
result_dict = {2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0, 12: 0}但写11行太累了!有没有更聪明的办法?
有!我们可以先准备好两个列表:
- 一个列表放所有的“键”(点数之和):
[2, 3, 4, ..., 12] - 一个列表放所有的“值”(初始次数):
[0, 0, 0, ..., 0](11个0)
然后,本节新增的魔法函数 zip() 就登场了!zip() 就像一条拉链,能把两个列表对应位置的元素“扣”在一起,形成一对对“键值对”。
# 看看 zip() 是怎么工作的
keys = [1, 2, 3]
values = ['a', 'b', 'c']
zipped = zip(keys, values)
print(list(zipped)) # 输出:[(1, 'a'), (2, 'b'), (3, 'c')][(1, 'a'), (2, 'b'), (3, 'c')]看,它把 1 和 'a'、2 和 'b' 分别打包成了元组(tuple)。但元组里的元素是不能修改的,而我们需要修改“次数”这个值。所以,我们再用 dict() 函数把这个“元组列表”变成真正的字典:
values = ['a', 'b', 'c']
keys = [1, 2, 3]
my_dict = dict(zip(keys, values))
print(my_dict) # 输出:{1: 'a', 2: 'b', 3: 'c'}{1: 'a', 2: 'b', 3: 'c'}太棒了!现在我们有了一个可以随时修改其中“值”的字典。
动手实现 2.0 版本
思路清晰了,我们来写代码。记住,我们只新增了 zip() 函数和字典的使用,其他部分都是1.0版本的“老朋友”。
import random
random.seed(42) # 固定随机种子,让示例输出可复现
def roll_dice():
"""
模拟掷一个骰子
"""
roll = random.randint(1, 6)
return roll
def main():
"""
主函数
"""
total_times = 10000 # 模拟投掷的总次数
# 1. 初始化一个全0的列表,长度为11(对应2到12共11种点数之和)
result_list = [0] * 11
# 2. 初始化点数列表,范围是2到12
# 注意:range(2, 13) 会生成 2, 3, 4, ..., 12,不包含13
point_list = list(range(2, 13))
# 3. 使用 zip() 和 dict() 创建字典:键是点数,值是初始次数0
result_dict = dict(zip(point_list, result_list))
# 4. 开始模拟投掷
for i in range(total_times):
# 掷第一个骰子
roll1 = roll_dice()
# 掷第二个骰子
roll2 = roll_dice()
# 计算点数之和
sum_of_points = roll1 + roll2
# 5. 在字典中对应的点数之和上增加一次计数
# 这里非常直接:点数之和 sum_of_points 就是字典的键
result_dict[sum_of_points] += 1
# 6. 输出结果
print(f'模拟投掷两个骰子 {total_times} 次,结果如下:')
for point, count in result_dict.items(): # .items() 可以同时取出键和值
frequency = count / total_times
print(f'点数{point}的次数:{count},频率:{frequency:.3f}') # :.3f 保留3位小数
# 运行主程序
main()运行上面的代码,你会看到下面的输出(代码固定了 random.seed(42),所以你运行会得到完全相同的数字;删掉这一行才会每次随机变化,但无论怎么变,下面这条“7 最多、2 和 12 最少”的规律都一致):
模拟投掷两个骰子 10000 次,结果如下:
点数2的次数:270,频率:0.027
点数3的次数:540,频率:0.054
点数4的次数:816,频率:0.082
点数5的次数:1094,频率:0.109
点数6的次数:1359,频率:0.136
点数7的次数:1704,频率:0.170
点数8的次数:1392,频率:0.139
点数9的次数:1116,频率:0.112
点数10的次数:838,频率:0.084
点数11的次数:560,频率:0.056
点数12的次数:311,频率:0.031发现有趣的规律
看输出结果!你发现什么了吗?
- 点数7出现的次数最多,频率最高(接近16.7%)。这很符合概率规律,因为两个骰子组合出7的方式最多(1+6, 2+5, 3+4, 4+3, 5+2, 6+1),有6种组合。
- 点数2和12出现的次数最少。因为它们各自只有一种组合方式(1+1 和 6+6)。
- 整体分布呈一个对称的“山峰”形状,中间高,两边低。
是不是很酷?我们不仅用程序模拟了游戏场景,还直观地验证了概率知识。字典的引入,让我们能轻松处理这种“键”不连续(2到12)的统计问题,比硬用列表方便多了。
📝 动手练一练
- 改变命运? 将代码中的
total_times从 10000 改为 1000 次再运行,观察频率(尤其是点数7的频率)变化大吗?这说明了“大数定律”的什么特点? - 挑战三个骰子! 如果我想模拟掷三个骰子,统计点数之和(3到18)的情况。按照本节课的思路,我需要初始化一个多长的列表?
point_list应该用range(?, ?)来生成?试着修改代码实现它。
👉 点击查看参考答案
- 思路:将
total_times改小后运行,会发现各点数的频率波动变大,可能不那么接近理论概率了。这说明实验次数越多,统计结果越稳定,越接近真实概率。
# 只需修改 main() 函数中的一行
total_times = 1000 # 改为1000次- 思路:三个骰子,点数之和最小为3(1+1+1),最大为18(6+6+6)。共有 18-3+1 = 16 种可能。
- 初始化列表长度应为16。
point_list = list(range(3, 19))# 生成3到18的列表- 同时需要掷三次骰子,并计算三个点数之和。
# 关键修改部分的代码片段
def main():
total_times = 10000
# 16种可能,从3到18
result_list = [0] * 16
point_list = list(range(3, 19))
result_dict = dict(zip(point_list, result_list))
for i in range(total_times):
roll1 = roll_dice()
roll2 = roll_dice()
roll3 = roll_dice() # 新增第三个骰子
sum_of_points = roll1 + roll2 + roll3
result_dict[sum_of_points] += 1
# ... 后续输出代码不变运行后你会发现,分布的山峰会更集中在中间(比如10或11附近)。本章小结
恭喜!你的“掷骰子模拟器”成功升级到了2.0版本,现在它能处理更复杂的双骰子游戏了。
📋 行动清单
- 运行代码,观察两个骰子点数之和的分布,记住7是最常见的点数。
- 理解
zip():在脑子里想象它像拉链一样合并两个列表的过程。 - 区分列表和字典:问自己,什么时候该用列表(索引连续),什么时候该用字典(键值对应更自由)?
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载第8章代码包:模拟掷骰子(含全部版本 5 个 .py)ZIP · 3KB