重要编辑:
如果您有时间测试下面的代码段,请务必开始一个全新的会话或致电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
如您所见,我的假设显然是错误的。什么真的在这里?
要点:
np.random.randint()
会为同一个随机状态返回不同的整数?感谢您的任何建议!
我的系统:
附录
如果将相同的过程包装到函数中并运行两次以上,则会得到相同的结果。
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
答案 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
的确切增长将取决于您请求的随机数的数据类型,因此pos
和pos
的确切增长不易预测(因此您在通过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
被重置。