为什么numpy会从同一个随机状态返回不同的随机数?

时间:2018-04-13 08:14:03

标签: python numpy

重要编辑: 如果您有时间测试下面的代码段,请务必开始一个全新的会话或致电np.random.seed(None)一次。

背景

我的印象是np.random.randint()之类的函数会为相同的随机状态(或任何你称之为np.random.get_state()的输出)绘制相同的数字集。

让我解释原因:

以下代码段使用np.random.randint()在-10和10之间生成5个随机整数,并存储有关该过程的一些信息。我所说的'州'是np.random.get_state()返回的元组中第二个元素中存储的数组中的5个第一个数字。

Snippet 1

# 1. Imports
import pandas as pd
import numpy as np

# 2. describe random state by
# retrieving the five first numbers
# in the array in the second element
# of the tuple returned by np.random.get_state()
randomState = np.random.get_state()
state = np.random.get_state()[1][:5]

# 3. generate random numbers
randints = np.random.randint(-10, 10, size = 5)

# 4. organize and present findings
df = pd.DataFrame.from_dict({'state':state, 'randints':randints})
print(df)

运行此代码一次,您将获得结果,如下面的第一个输出部分所示。请注意,由于没有设置随机种子,因此数字本身将与我的不同。重要的是三组输出的内部逻辑。如果你再次运行相同的代码片段,你会注意到我认为非常奇怪的事情:

输出1:一些随机数和一个随机状态:

   randints       state
0       -10  2871458436
1         7  4226334938
2         1   179611462
3        -9  3145869243
4         5   317931933

到目前为止,真好!我们有5个随机整数和5个代表随机状态的数字。 再次运行相同的代码段,您将获得以下内容:

输出2:新的随机数和新的随机状态:

   randints       state
0         1   727254058
1         7  1473793264
2         4  2934556005
3         1   721863250
4        -6  3873014002

现在你看似有一个新的随机状态和5个新的随机数。所以看起来,我的假设仍然存在。 但是每当我尝试这个时,当你第三次运行相同的代码时,事情变得越来越奇怪。看看这个:

输出3:新的随机数和与之前相同的随机状态:

   randints       state
0         8   727254058
1        -4  1473793264
2        -1  2934556005
3       -10   721863250
4        -1  3873014002

如您所见,我的假设显然是错误的。什么真的在这里?

要点:

  1. 为什么np.random.randint()会为同一个随机状态返回不同的整数?
  2. 为什么运行此代码段会为第一次和第二次运行产生不同的随机状态,但是为第二次和第三次运行返回相同的随机状态?
  3. 感谢您的任何建议!

    我的系统:

    • Python 3.6.0
    • IPython 5.1.0
    • Numpy 1.11.3
    • Spyder 3.2.7
    • Windows 64

    附录

    如果将相同的过程包装到函数中并运行两次以上,则会得到相同的结果。

    Snippet 2 - 与函数中包含的Snippet 1相同

    def rnumbers(numbers, runs):
    
        df_out = pd.DataFrame()
        runs = np.arange(runs)
    
        for r in runs:
    
            print(r)
    
            state = np.random.get_state()[1][:numbers]
    
            # 4. generate random numbers
            randints = np.random.randint(-10, 10, size = numbers)
    
            # 5. organize and present findings
            df_temp = pd.DataFrame.from_dict({'state_'+str(r+1):state, 'randints_'+str(r+1):randints})
    
            df_out = pd.concat([df_out, df_temp], axis = 1)
    
        return df_out
    
    df = rnumbers(10,3)
    print(df)
    

    输出:

       randints_1     state_1  randints_2     state_2  randints_3     state_3
    0           4  3582151794          -5  1773875493           7  1773875493
    1          -7  2910116392          -8  2402690106           3  2402690106
    2          -8  3435011439           3  1330293688           4  1330293688
    3           1   486242985           4   847834894           2   847834894
    4          -3  4214584559           4  4209159694          -2  4209159694
    5           4   752109368          -3  2673278965           1  2673278965
    6         -10  3726578976           8  2475058425           4  2475058425
    7           8  1510778984          -5  3758042425           0  3758042425
    8          -2  4202558983          -5  2381317628           0  2381317628
    9           4  1514856120           6  3177587154          -7  3177587154
    

2 个答案:

答案 0 :(得分:3)

总结一下这个问题:随机状态的一部分的前5个数字有时是相同的,但随机生成器的输出是不同的。

简短的回答是:随机状态 更改,但您正在查看的前5个数字保持不变。更改位于索引2处的数字:

for i in range(3):
    randomState = np.random.get_state()
    state = np.random.get_state()[2]
    randints = np.random.randint(-10, 10, size = 5)
    df = pd.DataFrame.from_dict({'state':state, 'randints':randints})
    print(df)

输出:

   randints  state
0        -9    624
1         6    624
2         4    624
3        -5    624
4         5    624
   randints  state
0        -9      5
1        -5      5
2         4      5
3        -4      5
4        -4      5
   randints  state
0         5     10
1        -8     10
2         8     10
3       -10     10
4        -3     10

Numpy使用Mersenne Twister算法,该算法一次生成624个随机数,每组624个。所以我们可能期望大状态数组保持不变,直到消耗掉所有这些数字并且需要再次调用Twister。

在州的索引2处,它存储已消耗了多少这些数字。这开始于624,因此Twister在开始时运行一次,然后生成任何输出。之后,您将看到列表保持不变,直到消耗了所有624个号码。然后再次调用Twister,计数器重置为0,整个事情重新开始。

答案 1 :(得分:3)

该行为的原因是,您只是检查状态向量是否相同。 RandomState还有另一个重要的部分,即位置pos,基本上表示状态向量已经用了多少"用完了#34;。它由返回值为get_state()的状态数组后的整数给出(参见docs of get_state())。请求的伪随机性的每个字节仅取决于状态向量的一个元素。元素的交叉依赖性仅在重新填充过程中出现。 (有关PRNG检查的更多详细信息,例如使用的Mersenne Twister的维基百科页面。)

在初始化过程中,矢量将根据种子填充,然后位置将设置为结束(如您所见here in numpy's sources)。

import numpy as np
state = np.random.get_state()
print(state[1].shape)           # (624, )
print(state[2])                 # 624

当您现在请求一个伪随机字节时,将调用this function,其中包括检查使用了多少向量。当pos设置为状态向量的长度减去1时,会触发重新填充并将pos设置为0.这就是为什么在{{1}中获得不同的数组的原因在您第一次拨打RandomState之后。

randint

对于后续调用,np.random.randint(10) state = np.random.get_state() print(state[1].shape) # (624, ) -> This is now different than before print(state[2]) # 1 始终小于向量的长度,因此,只有pos递增但向量不会重新填充。只有当您请求足够的随机数来耗尽pos中的数组时才会发生这种情况。

RandomState

但请注意,np.random.randint(10) state = np.random.get_state() print(state[1].shape) # (624, ) -> Now it did not change print(state[2]) # 3 的确切增长将取决于您请求的随机数的数据类型,因此pospos的确切增长不易预测(因此您在通过state[2]申请的每np.int32后,我们不能指望它增加1。

修改
我对上面例子中randint的非确定性增加感到有些困惑。这是由确保值在正确间隔内的方法引起的。 pos(假设randint为dtype)在内部调用np.int32,后者又调用rk_random_uint32,其中_rand_int32是一个参数,表示随机范围的宽度要绘制的整数。在此基础上,创建掩码以仅保留适当的位。如果现在你的范围不是2的幂,则仍然存在值(最后一位在rng和下一次幂2之间),如果它们被绘制并且随后被丢弃则无效。因此,根据种子,需要不同的尝试次数才能在正确的范围内找到有效数字。如果你选择一个2的幂范围,你会得到每个绘制随机数的预期增加1:

rng

在624个随机数后,状态向量用完,你可以看到In [1]: import numpy as np In [2]: print(np.random.get_state()[2]) 624 In [3]: for i in range(10): ...: np.random.randint(64, size=100, dtype=np.int32) ...: print(i, np.random.get_state()[2]) ...: 0 100 1 200 2 300 3 400 4 500 5 600 6 76 7 176 8 276 9 376 被重置。