Wrt 如何正确选择生成器的数字,在 Wiki 页面中有 Hull–Dobell Theorem 的描述,它告诉您如何选择 a 和 c 来拥有完整的周期生成器。您从数字食谱中获得了数字,据我所知,您将获得完整的 [0...232) 生成器。或者您可以查看来自this paper 的品质因数,有 (a,c) 对适用于任何所需的周期大小。
关于测试,请查看@pjs 提供的论文。
when it comes to choosing seeds, I was thinking about nanoseconds, but I have no idea how to implement it and will it make sense at all? The idea is to show that the selected seeds were chosen randomly and not so much from the cap。我认为这不是一个好主意,因为您不能保证从 time/ceil/... 挑选的种子不会重叠。 LCG基本上是双射的[0...232)[0...232)映射,随机数流比较容易重叠所以你的结果是相关的。
相反,我建议使用 LCG 的另一个不错的属性 - 对数向前(和向后)跳跃。因此,对于在N 内核上进行模拟,您可以只选择单个种子并在第一个代码上运行,相同的种子但为第二个内核跳过(N/232),种子和跳过(N/232 * 2) 以此类推。
具有显式状态和跳过的 LCG 代码如下,Win10 x64,Python 3.7 Anaconda
import numpy as np
class LCG(object):
UZERO: np.uint32 = np.uint32(0)
UONE : np.uint32 = np.uint32(1)
def __init__(self, seed: np.uint32, a: np.uint32, c: np.uint32) -> None:
self._seed: np.uint32 = np.uint32(seed)
self._a : np.uint32 = np.uint32(a)
self._c : np.uint32 = np.uint32(c)
def next(self) -> np.uint32:
self._seed = self._a * self._seed + self._c
return self._seed
def seed(self) -> np.uint32:
return self._seed
def set_seed(self, seed: np.uint32) -> np.uint32:
self._seed = seed
def skip(self, ns: np.int32) -> None:
"""
Signed argument - skip forward as well as backward
The algorithm here to determine the parameters used to skip ahead is
described in the paper F. Brown, "Random Number Generation with Arbitrary Stride,"
Trans. Am. Nucl. Soc. (Nov. 1994). This algorithm is able to skip ahead in
O(log2(N)) operations instead of O(N). It computes parameters
A and C which can then be used to find x_N = A*x_0 + C mod 2^M.
"""
nskip: np.uint32 = np.uint32(ns)
a: np.uint32 = self._a
c: np.uint32 = self._c
a_next: np.uint32 = LCG.UONE
c_next: np.uint32 = LCG.UZERO
while nskip > LCG.UZERO:
if (nskip & LCG.UONE) != LCG.UZERO:
a_next = a_next * a
c_next = c_next * a + c
c = (a + LCG.UONE) * c
a = a * a
nskip = nskip >> LCG.UONE
self._seed = a_next * self._seed + c_next
#%%
np.seterr(over='ignore')
a = np.uint32(1664525)
c = np.uint32(1013904223)
seed = np.uint32(1)
rng = LCG(seed, a, c)
print(rng.next())
print(rng.next())
print(rng.next())
rng.skip(-3) # back by 3
print(rng.next())
print(rng.next())
print(rng.next())
rng.skip(-3) # back by 3
rng.skip(2) # forward by 2
print(rng.next())
更新
生成 10k 个数字
np.seterr(over='ignore')
a = np.uint32(1664525)
c = np.uint32(1013904223)
seed = np.uint32(1)
rng = LCG(seed, a, c)
q = [rng.next() for _ in range(0, 10000)]