4.4 随机数生成

本节将简要介绍一种流行的程序应用—模拟和博弈。可以通过Python标准库的random模块模拟偶然因素

投掷六面骰子

可以通过生成10个1~6的随机数来模拟投掷10次六面骰子,如下:

为了使用random模块的功能,需要先导入模块。randrange函数随机生成一个从第一个参数到(但不包括)第二个参数之间的整数值。使用向上方向键导航到for语句,然后按Enter键重新运行程序,会显示不同的值,如下:

有些时候,例如在调试程序的时候,可能需要随机序列具有可重复性。本节的最后一部分将介绍如何使用random模块中的seed函数来保证随机序列重复出现。

投掷一个六面骰子6,000,000次

如果randrange可以生成真正的随机整数,那么每次调用它时,其范围内的每个数字都应该具有相同的出现概率(或称为机会可能性)。下面的脚本模拟了6,000,000次投掷,来验证点数1~6是否以相同的概率出现。运行脚本时,每个面应该出现大约1,000,000次,如例子的输出所示:

上面的脚本使用嵌套控制语句(嵌套在for语句中的if...elif语句)来确定骰子每个面出现的次数。for语句一共迭代了6,000,000次,可以使用Python的数字分隔符下划线(_)将数值6000000表示为6_000_000,使其更具可读性,而不能写成range(6,000,000),因为在Python的函数调用中使用逗号作为参数之间的分隔符,Python会将range(6,000,000)视为使用三个参数6、0和0调用函数range

根据每次投掷骰子的点数,脚本会将该点数对应的计数器变量加1。这个程序的运行可能需要几秒钟才能完成。多次运行程序,并观察运行结果,可以发现每次运行产生的结果都不尽相同。上面的脚本使用的if...elif语句中没有else子句。

为随机数生成器设置种子以保证可重复性

函数randrange实际上生成的是一个伪随机数。伪随机数是基于以一个称为seed的数值开头的内部计算生成的。因为每次启动新的交互式会话或执行的脚本包含随机模块中的函数时,Python都会使用不同的种子值[1],因此重复调用randrange会产生一系列看上去随机的数字。当调试的程序中包含有随机生成的数据时,最好使用相同的随机数序列进行调试,在排除了逻辑错误后,再使用其他的数据进行测试。为此,可以使用随机模块中的seed函数为随机数生成器设置种子,这样可以强制randrange从指定的种子开始计算其伪随机数序列。在下面的代码中,代码段[5]和代码段[8]会产生相同的结果,因为代码段[4]和代码段[7]中使用相同的种子(32):

代码段[6]则生成了不同的值,因为它是从代码段[5]开始的伪随机数序列的延续。


[1] 根据文档,Python生成种子值是基于系统时钟或依赖于操作系统的随机源。对于需要使用安全的随机数的应用程序,例如密码学,建议使用secrets模块,而不是random模块。