Python 通过子函数播种

Python seed through the subfunction

我有一个函数和一个子函数,它们中的每一个都生成了一些随机数组。为了使结果可重现,我使用了种子。但是我看到了奇怪的情况。

当我在子函数中有一个种子时,主函数中的随机数也受到子函数中种子的影响。而且,从主功能到子功能没有这种影响。 例如,考虑以下代码。

import random
from random import randint
import numpy as np


def randgene():

    a=np.random.randint(0,5,size=5)
    print "a in function", a
    np.random.seed(seed=15)
    b=np.random.randint(0,5,size=5)
    print "b in function", b

    d=np.random.choice(50, size=5, replace = False)
    print "d in function", d
    # np.random.seed(seed=None)

def main():

    print "d-without seed", np.random.choice(50, size=5, replace = False)
    print "a-without seed", np.random.randint(0,5,size=5)
    print "b-without seed", np.random.randint(0,5,size=5)
    f = randgene()    
    print "d-without seed", np.random.choice(50, size=5, replace = False)
    print "a-without seed", np.random.randint(0,5,size=5)
    print "b-without seed", np.random.randint(0,5,size=5)
    f = randgene()
    print "d-without seed", np.random.choice(50, size=5, replace = False)
    print "a-without seed", np.random.randint(0,5,size=5)
    print "b-without seed", np.random.randint(0,5,size=5)

    np.random.seed(seed=10)

    print "d-with seed", np.random.choice(50, size=5, replace = False)
    print "a-with seed", np.random.randint(0,5,size=5)
    print "b-with seed", np.random.randint(0,5,size=5)

    f = randgene()

    print "d-with seed", np.random.choice(50, size=5, replace = False)
    print "a-with seed", np.random.randint(0,5,size=5)

    f = randgene()

    print "d-with seed", np.random.choice(50, size=5, replace = False)
    print "a-with seed", np.random.randint(0,5,size=5)

if __name__ == '__main__':
    main()

对于这段代码,我得到了这样的结果:

d-without seed [14 29  9 42 18]
a-without seed [3 0 0 3 4]
b-without seed [3 2 0 2 1]
a in function [2 3 1 2 3]
b in function [0 4 0 4 3]
d in function [41 16 22 24 14]
d-without seed [ 8 21 32 39 11]
a-without seed [3 0 3 3 0]
b-without seed [1 2 2 1 4]
a in function [4 4 0 2 2]
b in function [0 4 0 4 3]
d in function [41 16 22 24 14]
d-without seed [ 8 21 32 39 11]
a-without seed [3 0 3 3 0]
b-without seed [1 2 2 1 4]
d-with seed [37 23 44 42 47]
a-with seed [2 0 0 4 4]
b-with seed [0 0 2 4 2]
a in function [0 0 2 3 0]
b in function [0 4 0 4 3]
d in function [41 16 22 24 14]
d-with seed [ 8 21 32 39 11]
a-with seed [3 0 3 3 0]
a in function [1 2 2 1 4]
b in function [0 4 0 4 3]
d in function [41 16 22 24 14]
d-with seed [ 8 21 32 39 11]
a-with seed [3 0 3 3 0]

其中,每当我调用子函数时,您都会在主函数中看到 d-with seed [ 8 21 32 39 11], a-with seed [3 0 3 3 0] 重复。 但是,如果我在子函数中注释 np.random.seed(seed=15) 行,我会得到这个结果:

d-without seed [17 20 23 36 28]
a-without seed [3 1 1 2 0]
b-without seed [3 2 1 1 3]
a in function [1 2 2 0 4]
b in function [4 4 0 4 2]
d in function [ 9 46 19  7 47]
d-without seed [39 42 10 17  4]
a-without seed [2 3 0 2 4]
b-without seed [1 4 1 3 2]
a in function [1 1 3 3 2]
b in function [1 3 4 4 3]
d in function [ 0  2 45  5 19]
d-without seed [24 20 47  3 29]
a-without seed [3 0 3 3 3]
b-without seed [1 0 0 2 3]
d-with seed [37 23 44 42 47]
a-with seed [2 0 0 4 4]
b-with seed [0 0 2 4 2]
a in function [0 0 2 3 0]
b in function [4 4 0 1 1]
d in function [ 6 11 35  4  7]
d-with seed [19 47 43 38 15]
a-with seed [0 4 2 1 2]
a in function [1 2 1 3 2]
b in function [3 4 4 0 2]
d in function [38 31 17 43  2]
d-with seed [ 7 15 39  2 49]
a-with seed [3 4 1 4 0]

显然没有重复。因此,主函数中的种子对子函数没有影响。 如果我同时保留 np.random.seed(seed=15)np.random.seed(seed=None) 未注释,我将得到类似的结果。 有人可以向我解释发生了什么吗?

提前致谢, 阿夫欣

随机数生成器种子用于设置其全局状态。该状态用于该点之后生成的所有数字(并且生成的每个数字都会导致状态发生变化)。您可以使用全局变量将您的示例与本示例进行比较:

def print_and_increment(): # this is like generating a random number
    global x
    print("x was {}. Incrementing...".format(x))
    x += 1
    print("x is now", x)

def func():
    global x
    print("func before seed")
    print_and_increment()

    x = 10 # this is equivalent to calling seed()

    print("func after seed")
    print_and_increment()

x = 0  # seed
print("top")
print_and_increment()

func()

print("middle")
print_and_increment()

func()

print("bottom")
print_and_increment()

如果你想让你的函数有自己的种子,它总是使用,但不改变外部使用的随机数生成器的状态,你可以使用 numpy.random.get_statenumpy.random.set_state .

尝试这样的事情:

def randgene():

    prev_state = np.random.get_state()

    a=np.random.randint(0,5,size=5)
    print "a in function", a
    np.random.seed(seed=15)
    b=np.random.randint(0,5,size=5)
    print "b in function", b

    d=np.random.choice(50, size=5, replace = False)
    print "d in function", d
    # np.random.seed(seed=None)

    np.random.set_state(prev_state)

现在您的函数应该不会影响外部代码对随机数生成器的使用。

对于局部随机状态,只需使用 numpy.random.RandomState 而不是 numpy.random.seed 设置的全局状态。

一个例子:

import numpy as np

seed1 = 1
seed2 = 42

rs1 = np.random.RandomState(seed1)
rs2 = np.random.RandomState(seed2)
rs3 = np.random.RandomState(seed1)  # same seed value used for rs1

a = rs1.randint(0, 5, size=5)
b = rs1.randint(0, 5, size=5)

c = rs2.randint(0, 5, size=5)

e = rs3.randint(0, 5, size=5)
f = rs3.randint(0, 5, size=5)


eq = np.testing.assert_array_equal

try:
    eq(a, b, err_msg='OK, a should be != b')
except AssertionError:
    pass

try:
    eq(a, c, err_msg='OK, a should be != c')
except AssertionError:
    pass

eq(a, e, err_msg='ERROR')  # a and e are equal due to same seed
eq(b, f, err_msg='ERROR')  # b and f are equal due to same seed

print('OK')