random.randint(1,10)会返回11吗?
在研究这个问题并查看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
,对吧?
3 个回答
来自Python文档:
几乎所有模块的功能都依赖于一个基本的函数random(),这个函数会生成一个随机的小数,范围是从0.0到1.0,但不包括1.0。
就像几乎所有生成随机小数的算法一样……
其他回答提到,random()
的结果总是严格小于 1.0
;不过,这只是部分真相。
如果你用 int(random() * n)
来计算 randrange(n)
,你还需要知道,对于任何满足 0.0 <= x < 1.0
的 Python 浮点数 x
和任何正整数 n
,都有 0.0 <= x * n < n
,因此 int(x * n)
一定小于 n
。
这里可能出错的地方有两个:首先,当我们计算 x * n
时,n
会被隐式转换为浮点数。对于足够大的 n
,这种转换可能会改变值。但是如果你查看 Python 的源代码,会发现它只在 n
小于 2**53
的情况下使用 int(random() * n)
方法(这里和下面假设平台使用的是 IEEE 754 双精度浮点数),这是转换 n
为浮点数时保证不会丢失信息的范围(因为 n
可以被准确表示为浮点数)。
第二个可能出错的地方是,乘法 x * n
的结果(现在是浮点数相乘)可能无法精确表示,因此会涉及一些舍入。如果 x
足够接近 1.0
,那么舍入可能会把结果向上舍入到 n
本身。
为了证明这种情况不会发生,我们只需要考虑 x
的最大可能值,即在几乎所有运行 Python 的机器上都是 1 - 2**-53
。所以我们需要证明 (1 - 2**-53) * n < n
对于我们的正整数 n
是成立的,因为总是会有 random() * n <= (1 - 2**-53) * n
。
证明(简述)设 k
为唯一的整数,使得 2**(k-1) < n <= 2**k
。那么 n
的下一个浮点数是 n - 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**-53
。但是 2**k - n < n
(因为我们选择了 k
使得 2**(k-1) < n
),所以这个乘积确实离 n - 2**(k-53)
更近,因此它会被舍入到下方(假设平台采用某种形式的四舍五入)。
所以我们是安全的。呼!
附录(2015-07-04):上述假设使用的是 IEEE 754 binary64 算法,采用四舍五入到偶数的舍入模式。在许多机器上,这个假设是相当安全的。然而,在使用 x87 FPU 进行浮点运算的 x86 机器上(例如各种 32 位 Linux),在乘法中可能会发生 双重舍入,这使得 random() * n
在 random()
返回最大可能值的情况下有可能向上舍入到 n
。发生这种情况的最小 n
是 n = 2049
。更多讨论请见 http://bugs.python.org/issue24546。
来自 random.py
和文档的内容:
"""Get the next random number in the range [0.0, 1.0)."""
这里的 )
表示这个区间是不包括 1.0 的。也就是说,它永远不会返回 1.0。
这是数学中的一个通用规则,[
和 ]
表示包括这个数,而 (
和 )
表示不包括这个数。这两种括号可以混合使用,比如 (a, b]
或 [a, b)
。想了解更多,可以看看 维基百科:区间(数学) 上的正式解释。