10

在研究这个问题并阅读 中的源代码时random.py,我开始怀疑是否randrange并且randint真的表现得像“广告”。我非常倾向于这样认为,但我阅读它的方式randrange基本上是实现为

start + int(random.random()*(stop-start))

start(假设和的整数值stop),所以randrange(1, 10)应该返回一个介于 1 和 9 之间的随机数。

randint(start, stop)正在调用randrange(start, stop+1),从而返回一个介于 1 和 10 之间的数字。

我现在的问题是:

如果random()曾经返回1.0,那么randint(1,10)会返回11,不是吗?

4

3 回答 3

27

来自random.py和文档:

"""Get the next random number in the range [0.0, 1.0)."""

)表示间隔是独占1.0 。也就是说,它永远不会返回 1.0。

这是数学中的一般约定,[]包容性的,而()是排斥性的,两种类型的括号可以混合为(a, b]or [a, b)。查看wikipedia: Interval (mathematics)以获得正式解释。

于 2010-06-14T14:19:14.567 回答
12

其他答案指出,结果random()总是严格小于1.0;然而,这只是故事的一半。

如果您计算randrange(n)int(random() * n),您需要知道对于任何x满足的 Python 浮点数0.0 <= x < 1.0和任何正整数n,确实0.0 <= x * n < n,所以int(x * n)严格小于n

这里有两件事可能会出错:首先,当我们计算时x * nn会隐式转换为浮点数。对于足够大的n,该转换可能会改变值。但是,如果您查看 Python 源代码,您会发现它只使用小于的int(random() * n)方法(这里和下面我假设平台使用 IEEE 754 双精度),这是转换为浮点数的范围保证不会丢失信息(因为可以精确地表示为浮点数)。n2**53nn

可能出错的第二件事是乘法的结果x * n(记住,现在作为浮点数的乘积执行)可能无法精确表示,因此会涉及一些舍入。如果x足够接近1.0,则可以想象舍入会将结果舍入到n自身。

为了避免这种情况发生,我们只需要考虑 的最大可能值x,即(在 Python 运行的几乎所有机器上)1 - 2**-53。所以我们需要证明(1 - 2**-53) * n < n我们的正整数n,因为它总是正确的random() * n <= (1 - 2**-53) * n

证明(草图) 让k是唯一的整数k,使得2**(k-1) < n <= 2**k。然后下一个浮动nn - 2**(k-53)。我们需要证明n*(1-2**53)(即产品的实际、未四舍五入的值)n - 2**(k-53)比更接近n,因此它总是被四舍五入。但是一点算术表明,从n*(1-2**-53)到的距离n是,而从到2**-53 * n的距离是。但是(因为我们选择了),所以产品接近,所以它向下舍入(假设平台正在做某种形式的舍入到最近)。n*(1-2**-53)n - 2**(k-53)(2**k - n) * 2**-532**k - n < nk2**(k-1) < nn - 2**(k-53)

所以我们是安全的。呸!


附录(2015-07-04):以上假设 IEEE 754 binary64 算术,具有round-ties-to-even舍入模式。在许多机器上,这种假设是相当安全的。但是,在使用 x87 FPU 进行浮点运算的 x86 机器上(例如,各种风格的 32 位 Linux),在乘法中可能会进行双舍入,这使得在这种情况下可以向上random() * n舍入where返回最大的可能值。可能发生这种情况的最小的情况是. 有关更多信息,请参阅http://bugs.python.org/issue24546上的讨论。nrandom()nn = 2049

于 2010-06-14T21:26:03.537 回答
3

来自 Python 文档:

几乎所有的模块函数都依赖于基本函数random(),它在半开范围[0.0, 1.0)内均匀地生成一个随机浮点数。

就像几乎所有浮点数的 PRNG 一样。

于 2010-06-14T14:19:29.293 回答