【问题标题】:Why does numpy return different random numbers from the same random state?为什么 numpy 从相同的随机状态返回不同的随机数?
【发布时间】:2018-09-23 12:50:56
【问题描述】:

重要修改: 如果您有时间测试下面的 sn-ps,请确保开始一个全新的会话或致电 np.random.seed(None) 一次。

背景:

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

让我解释一下原因:

下面的 sn-p 使用np.random.randint() 生成 5 个介于 -10 和 10 之间的随机整数,并存储有关该过程的一些信息。我命名为“状态”的是数组中的前 5 个数字,该数组存储在 np.random.get_state() 返回的元组的第二个元素中。

片段 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)

运行此代码一次,您将获得如下第一个输出部分所示的结果。请注意,数字本身将与我的不同,因为没有设置随机种子。重要的是三组输出的内部逻辑。如果你多次运行相同的 sn-p,你会发现一些我认为非常奇怪的东西:

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

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

到目前为止,一切都很好!我们有 5 个随机整数和 5 个代表随机状态的数字。 再次运行同样的 sn-p,你会得到这样的结果:

输出 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. 为什么运行此 sn-p 会在第一次和第二次运行时产生不同的随机状态,但在第二次和第三次运行时返回相同的随机状态?

感谢您的任何建议!

我的系统:

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

附录:

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

片段 2 - 与包含在函数中的片段 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

【问题讨论】:

  • np.random.seed(None) 应该做什么?删除它,您的输出将保持一致。至于片段二 - 我无法确认您的输出。 Mine is the same for all three rounds.
  • 我编辑了这个问题。这里重要的一点是尚未设置随机种子。这只是我提出这个问题之前的众多测试之一。是的,我知道np.random.seed(None)
  • 1.它不应该。 2.你确定随机状态是一样的吗?尝试打印出整个randomState。对于不同的运行,我得到了不同的 randomState(注意:不是你的第二个 sn-p,因为你已经设置了 random.seed,所以应该为不同的运行提供相同的 randomState,就像 T 先生一样。)
  • 在此处查看 Robert Kern 的回复:stackoverflow.com/questions/5836335/… 和此处:stackoverflow.com/questions/37224116/…
  • np.random.get_state() 中的 pos 编号 [2] 在不同的运行中会发生变化。那(我在这里猜)取之前数组中的相应值来设置随机状态。要验证,请使用np.random.set_state(x) 设置会话的随机状态,其中 x 是复制粘贴的,并将数组部分转换为 dtype='uint32' 的 np.array,用于 unsigned int32。然后,将 pos 值设置为 1,并使用数组中除 [1] 之外的其他值。您将获得相同的 randint 或任何其他随机函数。现在,如果你改变 pos 或 pos 指示的值,输出就会改变。

标签: python numpy


【解决方案1】:

所以总结一下问题:随机状态的一部分的前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 个一组的 32 位随机数。所以我们可能期望大状态数组保持不变,直到所有这些数字都被消耗掉并且需要再次调用 Twister。

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

【讨论】:

  • 感谢您接受,但我个人更喜欢@jotasi 的回答 :) 它更深入,并详细介绍了 pos 的微妙之处,因为我得到了幸运卷,所以我一定忽略了这一点。
【解决方案2】:

这种行为的原因是,您只是在检查状态向量是否相同。 RandomState 还有一个重要的部分,即pos 的位置,基本上表示状态向量有多少已经“用完”。它由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 被设置为状态向量的长度减一,因此触发了重新填充并将pos 设置为0。这就是为什么您在第一次调用randint 后在RandomState 中得到一个不同的数组的原因.

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

但是请注意,pos 的确切增加将取决于您请求的随机数的数据类型,因此 posstate[2] 的确切增加不容易预测(因此您不能期望它增加在您通过randint 请求的每个np.int32 之后加1。

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

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

624个随机数后,状态向量用完,可以看到pos被重置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-02
    • 1970-01-01
    • 2021-11-11
    • 2014-09-24
    • 1970-01-01
    相关资源
    最近更新 更多