【问题标题】:Random number in the range 1 to sys.maxsize is always 1 mod 2^101 到 sys.maxsize 范围内的随机数始终为 1 mod 2^10
【发布时间】:2016-02-09 16:17:59
【问题描述】:

我正在尝试通过使用频率测试、运行测试和卡方测试来查找 Python (2.7.10) 中可用的 PRNG 的统计属性。

为了进行频率测试,我需要将生成的随机数转换为其二进制表示,然后计算1's 和0's 的分布。我在 python 控制台上试验随机数的二进制表示并观察到这种奇怪的行为:

>>> for n in random.sample(xrange(1, sys.maxsize), 50):
...     print '{0:b}'.format(n)
... 
101101110011011001110011110110101101101101111111101000000000001
110000101001001011101001110111111110011000101011100010000000001
110111101101110011100010001010000101011111110010001110000000001
100001111010011000101001000001000011001111100000001010000000001
1111000010010011111100111110110100100011110111010000000000001
111000001011101011101110100001001001000011011001110110000000001
1000100111011000111000101010000101010100110111000100000000001
11101001000001101111110101111011001000100011011011010000000001
110011010111101101011000110011011001110001111000001010000000001
110110110110111100011111110111011111101000011001100000000001
100010010000011101011100110101011110111100001100100000000000001
10111100011010011010001000101011001110010010000010010000000001
101011100110110001010110000101100000111111011101011000000000001
1111110010110010000111111000010001101011011010101110000000001
11100010101101110110101000101101011011111101101000010000000001
10011110110110010110011010000110010010111001111001010000000001
110110011100111010100111100100000100011101100001100000000000001
100110011001101011110011010101111101100010000111001010000000001
111000101101100111110010110110100110111001000101000000000000001
111111101000010111001011111100111100011101001011010000000001
11110001111100000111010010011111010101101110111001010000000001
100001100101101100010101111100111101111001101010101010000000001
11101010110011000001101110000000001111010001110111000000000001
100111000110111010001110110101001011100101111101010000000001
100001101100000011101101010101111111011010111110111110000000001
100010010011110110111111111000010001101100111001001100000000001
110011111110010011000110101010101001001010000100011010000000001
1111011010100001001101101000011100001011001110010100000000001
110110011101100101001100111010101111001011111101100000000000001
1010001110100101001001011111000111011100001100000110000000001
1000101110010011011000001011010110001000110100100100000000001
11111110011001011100111110110111000001000100100010000000000001
101111101010000101010111111111000001100101111001011110000000001
10010010111111111100000001010010101100111001100000000000001
111110000001110010001110111101110101010110001110000000000000001
100000101101000110101010010000101101000011111010001110000000001
101001011101100011001000011010010000000111110111100010000000001
10110101010000111010110111001111011000001111001100110000000001
10110111100100100011100101001100000000101110100100010000000001
10010111110001011101001110000111011010110100110111110000000001
111011110010110111011011101011001100001000111001010100000000001
101001010001010100010010010001100111101110101111000110000000001
101011111010000101010101000110001101001001011110000000000001
1010001010111101101010111110110110000001111101101110000000001
10111111111010001000110000101101010101011010101100000000001
101011101010110000001111010100100110000011111100100100000000001
111100001101111010100111010001010010000010110110010110000000001
100111111000100110100001110101000010111111010010010000000000001
100111100001011100011000000000101100111111000111100110000000001
110110100000110111011101110101101000101110111111010110000000001
>>> 

如您所见,所有数字都以0000000001 结尾,即所有数字都是1 mod 2^10。为什么会这样?

此外,当范围为 1 to sys.maxsize 时,会观察到此行为。如果将范围指定为1 to 2^40,则不会观察到这一点。我想知道这种行为的原因以及我的代码是否有问题。

实现我正在使用的 PRNG 的随机库的文档是 here

如果我需要提供更多信息,请告诉我。

【问题讨论】:

  • 有趣的是,把xrange(1, sys.maxsize)改成xrange(k, sys.maxsize),所有数字都等于k mod 2^10
  • @iobender,是的,这从我在回答中勾勒出来的内容得出:int() 总是返回一个带有“很多”尾随 0 位的整数。它用作xrange(k, ...) 的索引。但是xrange(k, ...)[i] == i+k,所以如果i 有很多尾随0 位,那么i+k 的最后几位就是k

标签: python python-2.7 random


【解决方案1】:

@roeland 暗示了原因:在 Python 2 中,sample() 反复使用 int(random.random() * n)。查看源代码(在您的 Python 的Lib/random.py 中)以获取完整的详细信息。简而言之,random.random() 返回不超过 53 个有效(非零)前导位;然后int() 用零填充其余的低位(您显然在sys.maxsize == 2**63 - 1 的机器上);然后用具有“很多”低位 0 位的偶数索引您的基数 (xrange(1, sys.maxsize)) 始终返回具有相同数量的低位 0 位的奇数整数(最后一个除外)。

在 Python 3 中,这些都不会发生 - Python 3 中的 random 使用更强大的算法,并且仅在必要时回退到 random.random()。例如这里在 Python 3.4.3 下:

>>> hex(random.randrange(10**70))
'0x91fc11ed768be3a454bd66f593c218d8bbfa3b99f6285291e1d9f964a9'
>>> hex(random.randrange(10**70))
'0x7b07ff02b6676801e33094fca2fcca7f6e235481c479c521643b1acaf4'

编辑

这是一个更直接相关的示例,在 3.4.3 下,64 位框:

>>> import random, sys
>>> sys.maxsize == 2**63 - 1
True
>>> for i in random.sample(range(1, sys.maxsize), 6):
...    print(bin(i))
0b10001100101001001111110110011111000100110100111001100000010110
0b100111100110110100111101001100001100110001110010000101101000101
0b1100000001110000110100111101101010110001100110101111011100111
0b111110100001111100101001001001101101100100011001001010100001110
0b1100110100000011100010000011010010100100110111001111100110100
0b10011010000110101010101110001000101110111100100001111101110111

在这种情况下,Python 3 根本不调用 random.random(),而是从底层 Mersenne Twister 迭代地获取 32 位块(32 位无符号整数是这种 MT 实现的“自然”输出),将它们粘贴在一起以构建合适的索引。因此,在 Python 3 中,平台浮动与它无关;在 Python 2 中,浮动行为的怪癖与它有关。

【讨论】:

    【解决方案2】:

    它确实看起来像 random.sample 中的舍入误差。

    在乘以范围的扩展 (maxsize -1) 后,底部 4 位左右总是为零,然后当添加范围的开始 (1) 时,它们总是 1

    如果乘法工作正常,考虑到扩展不是 2 的幂,并且随机数只有 53 个不同的位,我希望在最右边的位中也能看到不同的值。

    【讨论】:

    • 它们确实以 10 为底,但以 2 为底不同:每个“足够大”的浮点数都是一个带有一堆尾随 0 位的精确整数。尝试任何 - 例如,hex(int(2.22e22)) == '0x4b376e3ab5be8000000L'。乘法是一条红鲱鱼 - 重要的是结果(无论如何获得)大于 2**53。
    【解决方案3】:

    这取决于很多事情,例如 RNG 的具体实现方式、它使用多少位状态以及 sample 函数的具体实现方式。

    文档是这样说的:

    几乎所有的模块函数都依赖于基本函数random(),它在半开范围[0.0, 1.0)内均匀地生成一个随机浮点数。 Python 使用 Mersenne Twister 作为核心生成器。它产生 53 位精度浮点数,周期为 2**19937-1。

    因此,如果 sample 确实在后台使用了 random(),那么您应该只期望结果中有 53 位有意义的位。

    【讨论】:

      猜你喜欢
      • 2010-11-26
      • 1970-01-01
      • 1970-01-01
      • 2017-08-20
      • 1970-01-01
      • 1970-01-01
      • 2013-11-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多