随机

随机 属于 Python

{other: "%{count} 个练习"}

关于 随机

许多程序需要用(看似)随机的值来模拟现实世界中的事件。

常见、熟悉的例子包括:

  • 抛硬币:从 ('Heads', 'Tails')中随机取一个值。
  • 掷骰子:从 1 到 6 中随机取一个整数。
  • 洗牌:把一副牌随机打乱顺序。

让计算机生成真正随机的值是一项出奇困难的技术挑战,所以你会看到这类结果被称为“伪随机”。

实际上,一个设计良好的库,比如 Python 标准库中的 random模块,既快速又灵活,它给出的结果对于建模、模拟和游戏中的大多数应用来说都完全够用。

本页的其余部分会列出 random中几个最常用的函数。 我们鼓励你探索完整的 random文档,因为其中的选项远比这里介绍的要多。

Caution

random模块__不应__用于安全或加密用途。

Python 为此提供了 secrets模块。 它专门针对加密安全进行了优化。 创建 secrets 模块的一些先前问题和原因可以在 PEP 506 中找到。

导入

在使用 random模块中的工具之前,必须先导入它:

>>> import random

# Choose random integer from a range
>>> random.randrange(1000)
360

>>> random.randrange(-1, 500)
228

>>> random.randrange(-10, 11, 2)
-8

# Choose random integer between two values (inclusive)
>>> random.randint(5, 25)
22

为了避免反复输入模块名,你可以按名称导入特定的函数:

>>> from random import choice, choices

# Using choice() to pick Heads or Tails 10 times
>>> tosses = []
... for side in range(10):
...    tosses.append(choice(['H', 'T']))    

>>> print(tosses)
['H', 'H', 'H', 'H', 'H', 'H', 'H', 'T', 'T', 'H']


# Using choices() to pick Heads or Tails 8 times
>>> picks = []
... picks.extend(choices(['H', 'T'], k=8))
... print(picks)
['T', 'H', 'H', 'T', 'H', 'H', 'T', 'T']

生成随机整数

randrange()函数有三种形式,用于从 range(start, stop, step)中选取一个随机值:

  1. randrange(stop) 给出一个整数 n,满足 0 <= n < stop
  2. randrange(start, stop) 给出一个整数 n,满足 start <= n < stop
  3. randrange(start, stop, step) 给出一个整数 n,满足 start <= n < stop,且 n 位于序列 start, start + step, start + 2*step... 中

对于 step == 1 这一常见情况,randint(a, b)函数可能更方便、更易读。 randint()的可能结果_包含_上界,因此 randint(a, b)等同于使用 randrange(a, b+1):

>>> import random

# Select one number at random from the range 0, 499
>>> random.randrange(500)
219

# Select 10 numbers at random between 0 and 9 two steps apart.
>>> numbers = []
... for integer in range(10):
...    numbers.append(random.randrange(0, 10, 2))

>>> print(numbers)
[2, 8, 4, 0, 4, 2, 6, 6, 8, 8]

# roll a die
>>> random.randint(1, 6)  
4

处理序列

本节中的函数假设你从一个序列或其他容器开始。

这通常是一个 list,或者在有一定限制的情况下,是一个 tuple 或 set(tuple 是不可变的,而 set 是无序的)。

choice()和choices()

choice()函数会从给定的序列中随机返回一个元素。 最简单的例子就是抛硬币:

# This will pick one of the two values in the list at random 5 separate times 
>>> [random.choice(['H', 'T']) for _ in range(5)]
['T', 'H', 'H', 'T', 'H']

我们也可以用 choices()函数达到基本相同的效果,只需提供一个表示数组长度的关键字参数:

>>> random.choices(['H', 'T'], k=5)
['T', 'H', 'T', 'H', 'H']

在上面的例子中,我们假设硬币是均匀的,正面和反面的概率相同,不过也可以指定权重。 例如,如果袋子里有 10 个红球和 15 个绿球,我们想随机取出一个:

>>> random.choices(['red', 'green'], [10, 15])
['red']

sample()

上面的 choices()例子假设的是统计学家所说的“有放回抽样”。 每一次抽取或选择都不会影响后续选择的概率,可供选择的分布在每次抽取之间保持不变。

在红球和绿球的例子中:每次选择后,我们把球_放回_袋中并摇匀,然后才进行下一次抽取。 这与取出一个红球后_它一直留在外面_的情况相反。 不放回球意味着袋中的红球变少了,下一次选择_更不可能_抽到红球。

为了模拟这种“无放回抽样”,random 模块提供了 sample()函数。 sample()的语法与 choices()类似,只是多了一个 counts 关键字参数:

>>> random.sample(['red', 'green'], counts=[10, 15], k=10)
['green', 'green', 'green', 'green', 'green', 'red', 'red', 'red', 'red', 'green']

样本按被选中的顺序返回。

shuffle()

当 k > 1 时,choices()和sample()都会返回新的数组。 相比之下,shuffle()会_原地_打乱数组的顺序,原来的顺序会丢失:

>>> my_list = [1, 2, 3, 4, 5]
>>> random.shuffle(my_list)
>>> my_list
[4, 1, 5, 2, 3]

处理分布

到目前为止,我们关注的都是所有结果等概率出现的情况。 例如,random.randrange(100)给出 0 到 99 之间任何整数的概率都相同。

许多现实世界的情况远比这复杂。 因此,统计学家创造了各种各样的 distributions,用数学方式描述“现实世界”的结果。

均匀分布

对于整数,当所有概率都相等时,使用 randrange()和randint()。 这被称为 uniform分布。

randrange()和randint()也有对应的浮点数版本。

random() 给出一个 float 值 x,满足 0.0 <= x < 1.0。

uniform(a, b) 给出 x,满足 a <= x <= b。

>>> [round(random.random(), 3) for _ in range(5)]
[0.876, 0.084, 0.483, 0.22, 0.863]

>>> [round(random.uniform(2, 5), 3) for _ in range(5)]
[2.798, 2.539, 3.779, 3.363, 4.33]

高斯分布

它也被称为“正态”分布或“钟形”曲线,是描述测量值不精确性的一种非常常见的方式。

例如,假设你工作的工厂刚买进 10,000 个本应完全相同的螺栓。 你想设置工厂机器人来处理它们,于是称量了 100 个作为样本,发现它们的平均(也就是 mean)重量为 4.731g。 这极不可能意味着它们全都恰好重 4.731g。 也许你发现数值范围从 4.627 到 4.794g,但集中分布在 4.731g 附近。

这就是 Gaussian distribution,它的概率在均值处达到峰值,并在两侧对称地逐渐减小(因此呈“钟形”)。 要在软件中模拟它,我们需要某种方式来指定曲线的宽度(通常,昂贵的螺栓会比便宜的螺栓更紧密地聚集在均值附近!)。

按照惯例,这通过 standard deviation来实现:值小则曲线又尖又窄,值大则曲线又低又宽。 数学家喜欢希腊字母,所以我们用 μ('mu')表示均值,用 σ('sigma')表示标准差。 因此,如果你读到“95% 的值都在 μ 的 2σ 以内”或“希格斯玻色子以 5-sigma 置信度被探测到”,这类说法都与标准差有关。

>>> mu = 4.731
>>> sigma = 0.316
>>> [round(random.gauss(mu, sigma), 3) for _ in range(5)]
[4.72, 4.957, 4.64, 4.556, 4.968]
通过 GitHub 编辑 该链接会在新窗口或标签页中打开