Python中随机数最后位数的分布
在Python中,有两种明显的方法可以生成从0到9的随机数字。可以生成一个介于0和1之间的随机浮点数,乘以10,然后向下取整。或者,可以使用Python中随机数最后位数的分布,python,random,Python,Random,在Python中,有两种明显的方法可以生成从0到9的随机数字。可以生成一个介于0和1之间的随机浮点数,乘以10,然后向下取整。或者,可以使用random.randint方法 import random def random_digit_1(): return int(10 * random.random()) def random_digit_2(): return random.randint(0, 9) 我很好奇,如果一个人生成一个介于0和1之间的随机数,并保留最后一个
random.randint
方法
import random
def random_digit_1():
return int(10 * random.random())
def random_digit_2():
return random.randint(0, 9)
我很好奇,如果一个人生成一个介于0和1之间的随机数,并保留最后一个数字,会发生什么。我不一定期望分布是均匀的,但我发现结果相当令人惊讶
from random import random, seed
from collections import Counter
seed(0)
counts = Counter(int(str(random())[-1]) for _ in range(1_000_000))
print(counts)
输出:
Counter({1: 84206,
5: 130245,
3: 119433,
6: 129835,
8: 101488,
2: 100861,
9: 84796,
4: 129088,
7: 120048})
柱状图如下所示。请注意,0不会出现,因为尾部的零会被截断。但有人能解释为什么数字4、5和6比其他数字更常见吗?我曾经
Python3.6.10,但在Python3.8.0a4中的结果类似
这不是数字的“最后一位”。这是传递数字时字符串str
给您的最后一位数字
当您在浮点上调用str
时,Python会为您提供足够的数字,以便在字符串上调用float
将为您提供原始浮点。为此,尾随1或9比其他数字更不必要,因为尾随1或9表示数字非常接近通过舍入该数字得到的值。很有可能没有其他浮点数更接近,如果是这样的话,可以在不牺牲float(str(original_float))
行为的情况下丢弃该数字
如果str
提供了足够的数字来精确表示参数,则最后一位数字几乎总是5,除非random.random()
返回0.0,在这种情况下,最后一位数字将是0。(浮点只能表示,非整数二元有理数的最后一个非零十进制数字总是5。)输出也会非常长,看起来像
>>> import decimal, random
>>> print(decimal.Decimal(random.random()))
0.29711195452007921335990658917580731213092803955078125
这就是str
不这样做的原因之一
如果str
正好给了您17个有效数字(足以区分所有浮点值,但有时数字比需要的多),那么您看到的效果就会消失。尾随数字(包括0)几乎均匀分布
(另外,您忘记了
str
有时会返回科学记数法中的字符串,但这只是一个小影响,因为从random.random()
)中获得浮点值的概率很低)TL;DR您的示例实际上并没有看到最后一位数字。转换为基数10的有限二进制尾数的最后一位应始终为0
或5
请看下面的评论:
char*PyOS\u double\u到字符串(double val,
字符格式\u代码,
整数精度,
int标志,
int*类型)
{
字符格式[32];
Py_ssize_t bufsize;
char*buf;
int t,exp;
整数上限=0;
/*验证格式代码,并映射大小写*/
开关(格式\u代码){
// ...
案例'r':/*repr格式*/
/*提供的精度未使用,必须为0*/
如果(精度!=0){
PyErr_BadInternalCall();
返回NULL;
}
/*repr()精度(17位有效十进制数字)是
保证有足够精度的最小数
所以如果这个数字是以完全相同的二进制读回的
值被重新创建。这对于IEEE浮点是正确的
这是设计上的,而且碰巧也适用于所有其他现代企业
硬件*/
精度=17;
格式_代码='g';
打破
// ...
}
确认这一点:
53位有效位精度提供15到17位有效十进制数字精度(2-53≈ 1.11 × 10-16)。如果将最多15位有效数字的十进制字符串转换为IEEE 754双精度表示,然后再转换回具有相同位数的十进制字符串,则最终结果应与原始字符串匹配。如果将IEEE 754双精度数字转换为至少17位有效数字的十进制字符串t位,然后转换回双精度表示,最终结果必须与原始数字匹配。
因此,当我们使用str
(或repr
)时,我们只表示以10为基数的17位有效数字。这意味着一些浮点数将被截断。事实上,要获得准确的表示,您需要53位有效数字的精度!您可以通过以下方式验证:
>>> counts = Counter(
... len(f"{random():.99f}".lstrip("0.").rstrip("0"))
... for _ in range(1000000)
... )
>>> counts
Counter({53: 449833,
52: 270000,
51: 139796,
50: 70341,
49: 35030,
48: 17507,
47: 8610,
46: 4405,
45: 2231,
44: 1120,
43: 583,
42: 272,
41: 155,
40: 60,
39: 25,
38: 13,
37: 6,
36: 5,
35: 4,
34: 3,
32: 1})
>>> max(counts)
53
现在使用最大精度,这里是找到“最后一位”的正确方法:
因此,最后一个数字总是5
(或者在极少数情况下,0
),这是有意义的,因为:
2**0 == 1.0
2**-1 == 0.5
2**-2 == 0.25
2**-3 == 0.125
2**-4 == 0.0625
2**-5 == 0.03125
2**-6 == 0.015625
2**-7 == 0.0078125
2**-8 == 0.00390625
2**-9 == 0.001953125
...
2**-k == 0.[k-1 digits]5
所有尾数都是这些系数的部分和
注意:正如用户2357112所指出的,要查看的正确实现是和,但我将保留当前的实现,因为它在教学上更有趣。这与Python中计算浮点数的字符串表示的方式有关。请参阅。如果使用十分之一的digi,您将获得更均匀的结果t(小数点后的第一位)而不是最后一位数字。我们以二进制表示形式存储浮点数(因为我们的内存也是二进制的)。
str
将其转换为基数-10,这必然会导致问题。例如,1位浮点数尾数b0->1.0
和b1->1.5
。“最后一位”将始终是0
或5
随机。随机范围(10)更为明显,IMHO。随机。随机范围
(在引擎盖下调用random.randrange
)是随机
模块的一个后续添加,适用于不了解范围如何工作的人
2**0 == 1.0
2**-1 == 0.5
2**-2 == 0.25
2**-3 == 0.125
2**-4 == 0.0625
2**-5 == 0.03125
2**-6 == 0.015625
2**-7 == 0.0078125
2**-8 == 0.00390625
2**-9 == 0.001953125
...
2**-k == 0.[k-1 digits]5