为什么包装随机方法随机.随机似乎会影响RNG?



我试图通过在它周围放置一个包装器来打印东西来记录对random()的调用。令人惊讶的是,我注意到我开始获得不同的随机值。我创建了一个小示例来演示该行为:

脚本 main.py:

import random
def not_wrapped(seed):
""" not wrapped """
gen = random.Random()
gen.seed(seed)
# def wrappy(func):
#     return lambda: func()
# gen.random = wrappy(gen.random)
gen.randint(1, 1)
return gen.getstate()
def wrapped(seed):
""" wrapped """
gen = random.Random()
gen.seed(seed)
def wrappy(func):
return lambda: func()
gen.random = wrappy(gen.random)
gen.randint(1, 1)
return gen.getstate()
for s in range(20):
print(s, not_wrapped(s) == wrapped(s))

python3.7.5 main.py的输出(与python 3.6.9相同)

0 True
1 False
2 False
3 False
4 False
5 True
6 False
7 False
8 False
9 False
10 True
11 False
12 False
13 False
14 False
15 True
16 False
17 True
18 False
19 True

因此,正如您所看到的,随机实例gen的状态是不同的,这取决于我是否将 gen.random 包装在wrappy中。

如果我打电话randint()两次,则所有种子 0-19 的测试都将失败。对于 Python 2.7.17,wrappednot_wrapped函数每次都返回相同的随机值(为每个种子打印 True)。

有谁知道发生了什么?

Python 3.6 在线示例:http://tpcg.io/inbKc8hK

在这个在线 repl 的 Python 3.8.2 上,问题没有显示:https://repl.it/@DavidMoberg/ExemplaryTeemingDos

(此问题已交叉发布于:https://www.reddit.com/r/learnpython/comments/i597at/is_there_a_bug_in_randomrandom/)

这个答案是关于Python 3.6的。

状态仅由gen.randint(1, 1)调用更改,因此让我们看看它在引擎盖下调用的内容。

  1. 以下是random.Random.randint的实现:

    def randint(self, a, b):
    """Return random integer in range [a, b], including both end points.
    """
    return self.randrange(a, b+1)
    
  2. random.Random.randrange就在上面,它使用random.Random._randbelow生成随机数......

  3. 。它randint正下方实现,并检查random方法是否已被覆盖!

看起来问题_randbelow

...
from types import MethodType as _MethodType, BuiltinMethodType as _BuiltinMethodType
...

def _randbelow(self, n, int=int, maxsize=1<<BPF, type=type,
Method=_MethodType, BuiltinMethod=_BuiltinMethodType):
"Return a random int in the range [0,n).  Raises ValueError if n==0."
random = self.random
getrandbits = self.getrandbits
# CHECKS IF random HAS BEEN OVERRIDDEN!
# If not, this is the default behaviour:
# Only call self.getrandbits if the original random() builtin method
# has not been overridden or if a new getrandbits() was supplied.
if type(random) is BuiltinMethod or type(getrandbits) is Method:
k = n.bit_length()  # don't use (n-1) here because n can be 1
r = getrandbits(k)          # 0 <= r < 2**k
while r >= n:
r = getrandbits(k)
return r
# OTHERWISE, it does something different!
# There's an overridden random() method but no new getrandbits() method,
# so we can only use random() from here.
# And then it goes on to use `random` only, no `getrandbits`!

(作为参考,getrandbits在这里用 C 实现,random在这里也是用 C 实现的。

所以有区别:randint方法最终的行为会有所不同,具体取决于random是否被覆盖。

最新更新