我注意到您可以在 中输入各种数字,numpy.random.seed()
例如. 不同的数字是什么意思?你如何选择数字?numpy.random.seed(1)
numpy.random.seed(101)
7 回答
考虑一个非常基本的随机数生成器:
Z[i] = (a*Z[i-1] + c) % m
在这里,Z[i]
是ith
随机数,a
是乘数,c
是增量 - 对于不同的a
,c
和m
组合,您有不同的生成器。这被称为Lehmer 引入的线性同余生成器。该除法的其余部分或模数%
(m-1
U[i] = Z[i] / m
你可能已经注意到,为了开始这个生成过程——为了有一个Z[1]
你需要有Z[0]
一个初始值。启动该过程的这个初始值称为种子。看看这个例子:
初始值,种子被确定为 7 以启动该过程。但是,该值不用于生成随机数。相反,它用于生成第一个Z
.
伪随机数生成器最重要的特征是它的不可预测性。一般来说,只要你不分享你的种子,你对所有种子都很好,因为今天的生成器比这复杂得多。但是,作为进一步的步骤,您也可以随机生成种子。您可以跳过第一个n
数字作为另一种选择。
主要来源:Law, AM (2007)。仿真建模和分析。塔塔麦格劳-希尔。
简短的回答:
seed()
随机数生成器有以下三种方式numpy.random
:
不使用参数或使用
None
- RNG 从操作系统的随机数生成器(通常是加密随机的)初始化自身使用一些 32 位整数 N - RNG 将使用它来基于确定性函数初始化其状态(相同的种子 → 相同的状态)
使用类似数组的 32 位整数序列 n 0、 n 1、 n 2等——同样,RNG 将使用它来基于确定性函数初始化其状态(种子值相同→相同状态)。这旨在通过某种散列函数来完成,尽管源代码中有神奇的数字,并且不清楚他们为什么要这样做。
如果您想做一些可重复且简单的事情,请使用单个整数。
如果您想做一些可重复但第三方不太可能猜测的事情,请使用元组或列表或包含一些 32 位整数序列的 numpy 数组。例如,您可以使用numpy.random
种子None
从操作系统的 RNG 生成一堆 32 位整数(例如,其中 32 个整数,这将生成总共 1024 位),存储在一些种子S
中,您可以将其保存在一些秘密的地方,然后使用该种子生成您想要的任何伪随机数序列 R。然后您可以稍后通过再次重新播种来重新创建该序列,S
只要您保留S
秘密(以及生成的数字 R),没有人能够重现该序列 R。如果你只使用一个整数,那么只有 40 亿种可能性,有人可能会尝试所有这些。这可能有点偏执,但你可以做到。
更长的答案
该numpy.random
模块使用Mersenne Twister算法,您可以通过以下两种方式之一来确认自己:
通过查看 的文档
numpy.random.RandomState
,其中numpy.random
使用numpy.random.*
函数的实例(但您也可以使用孤立的独立实例)查看mtrand.pyx中的源代码,它使用名为 Pyrex 的东西来包装快速 C 实现,以及randomkit.c和initarray.c。
无论如何,这就是numpy.random.RandomState
文档所说的seed()
:
兼容性保证使用相同参数的固定种子和对方法的一系列固定调用
RandomState
将始终产生相同的结果,直至舍入误差,除非值不正确。不正确的值将被修复,并且修复的 NumPy 版本将在相关文档字符串中注明。只要先前的行为保持不变,就允许扩展现有参数范围和添加新参数。参数:
种子 :{None, int, array_like},可选用于初始化伪随机数生成器的随机种子。可以是 0 到 2**32 - 1 (含)之间的任何整数、此类整数的数组(或其他序列)或
None
(默认值)。如果种子是None
,则 RandomState 将尝试从/dev/urandom
(或 Windows 模拟)读取数据(如果可用)或从时钟中读取数据,否则。
它没有说明种子是如何使用的,但是如果您深入研究源代码,它指的是init_by_array
函数:(省略了文档字符串)
def seed(self, seed=None):
cdef rk_error errcode
cdef ndarray obj "arrayObject_obj"
try:
if seed is None:
with self.lock:
errcode = rk_randomseed(self.internal_state)
else:
idx = operator.index(seed)
if idx > int(2**32 - 1) or idx < 0:
raise ValueError("Seed must be between 0 and 2**32 - 1")
with self.lock:
rk_seed(idx, self.internal_state)
except TypeError:
obj = np.asarray(seed).astype(np.int64, casting='safe')
if ((obj > int(2**32 - 1)) | (obj < 0)).any():
raise ValueError("Seed must be between 0 and 2**32 - 1")
obj = obj.astype('L', casting='unsafe')
with self.lock:
init_by_array(self.internal_state, <unsigned long *>PyArray_DATA(obj),
PyArray_DIM(obj, 0))
这是init_by_array
函数的样子:
extern void
init_by_array(rk_state *self, unsigned long init_key[], npy_intp key_length)
{
/* was signed in the original code. RDH 12/16/2002 */
npy_intp i = 1;
npy_intp j = 0;
unsigned long *mt = self->key;
npy_intp k;
init_genrand(self, 19650218UL);
k = (RK_STATE_LEN > key_length ? RK_STATE_LEN : key_length);
for (; k; k--) {
/* non linear */
mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1664525UL))
+ init_key[j] + j;
/* for > 32 bit machines */
mt[i] &= 0xffffffffUL;
i++;
j++;
if (i >= RK_STATE_LEN) {
mt[0] = mt[RK_STATE_LEN - 1];
i = 1;
}
if (j >= key_length) {
j = 0;
}
}
for (k = RK_STATE_LEN - 1; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
- i; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++;
if (i >= RK_STATE_LEN) {
mt[0] = mt[RK_STATE_LEN - 1];
i = 1;
}
}
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
self->gauss = 0;
self->has_gauss = 0;
self->has_binomial = 0;
}
这实质上是使用提供的种子值序列中的每个值以非线性、类似哈希的方法“处理”随机数状态。
实际上通常称为随机数序列的是“伪随机”数序列,因为这些值是使用确定性算法计算的,并且概率没有实际作用。
“种子”是序列的起点,保证如果您从同一个种子开始,您将获得相同的数字序列。这对于例如调试非常有用(当您在程序中寻找错误时,您需要能够重现问题并研究它,非确定性程序将更难调试,因为每次运行都会不同) .
要了解随机种子的含义,您需要首先了解“伪随机”数列,因为这些值是使用确定性算法计算的。
因此,您可以将此数字视为计算从随机生成器获得的下一个数字的起始值。在这里放置相同的值将使您的程序每次都获得相同的“随机”值,因此您的程序变得确定性。
正如这篇文章中所说
他们(
numpy.random
和random.random
)都使用 Mersenne twister 序列来生成他们的随机数,而且它们都是完全确定的——也就是说,如果你知道一些关键信息,就可以绝对确定地预测接下来会出现什么数字。
如果您真的关心随机性,请让用户产生一些噪音(一些任意词)或者只是将系统时间作为种子。
如果您的代码在 Intel CPU(或具有最新芯片的 AMD)上运行,我还建议您检查使用 cpu 指令收集“真实”(硬件)随机性的RdRand包。rdrand
参考:
附带评论:最好将您的种子设置为相当大的数字,但仍在生成器限制之内。这样做可以让种子数在 0 位和 1 位之间有一个很好的平衡。避免种子中有许多 0 位。
参考:pyTorch文档
一个非常具体的答案:np.random.seed
可以从 中获取值0 and 2**32 - 1
,有趣的是,它与random.seed
可以获取任何可散列对象的不同。
基本上,这个数字每次都能保证相同的“随机性”。
更准确地说,数字是种子,可以是整数、任意长度的整数数组(或其他序列)或默认值(无)。如果没有种子,那么 random 将尝试从 /dev/urandom 读取数据(如果可用),否则从时钟生成种子。
编辑:老实说,只要你的程序不是需要超级安全的东西,你选择什么都不重要。如果是这种情况,请不要使用这些方法 - 使用os.urandom()
或者SystemRandom
如果您需要加密安全的伪随机数生成器。
这里要理解的最重要的概念是伪随机性。一旦你理解了这个想法,你就可以确定你的程序是否真的需要种子等。我建议在这里阅读。