【问题标题】:Linear congruential generator - how to choose seeds and statistical tests线性同余生成器 - 如何选择种子和统计测试
【发布时间】:2019-11-20 22:34:50
【问题描述】:

我需要做一个能够成功通过所选统计测试的线性同余生成器。

我的问题是:如何正确选择生成器的数字和 我应该选择哪些统计测试?

我想过:

  1. 均匀性的卡方频率检验

    • 每个生成方法收集 10,000 个数字

    • 将 [0.1) 细分为 10 个相等的细分

  2. Kolmogorov-Smirnov 均匀性检验

    • 由于 K-S 检验更适用于较小的数字集,因此您可以使用为卡方频率检验生成的 10,000 个中的前 100 个

下面是代码示例:

def seedLCG(initVal):
    global rand
    rand = initVal

def lcg():
    a = 1664525
    c = 1013904223
    m = 2**32
    global rand
    rand = (a*rand + c) % m
    return rand

seedLCG(1)

for i in range(1000):
    print (lcg())

在选择种子时,我在考虑纳秒,但我不知道如何实现它,它是否有意义?这个想法是为了表明选择的种子是随机选择的,而不是从上限中选择的

【问题讨论】:

  • 这听起来像是一个家庭作业,在这种情况下,选择是你的,而不是我们的。维基百科有一个page,其中包含挑选系数的规则,以及一张常用系数表。对于测试,您提到的两个是相当标准的。如果您需要更多替代方案,请参阅Diehard tests。对于播种,time.time_ns() 在 Python 3.7 中可用。最后,查看sciencedirect.com/science/article/pii/0167637786900921 进行的测试,该测试未能通过返回完整种子的 LCG。
  • 您认为使用time.time_ns()选择初始参数是一个好的解决方案还是从标准(从表中)中选择一些更好?
  • 很棒的文章。谢谢!
  • time.time_ns() 将用于播种,而不是用于选择 LCG 参数化。那是how Java does it (see lines 114-135)
  • 您喜欢哪篇文章?我在那里有几个链接......

标签: python random lcg


【解决方案1】:

Wrt 如何正确选择生成器的数字,在 Wiki 页面中有 Hull–Dobell Theorem 的描述,它告诉您如何选择 ac 来拥有完整的周期生成器。您从数字食谱中获得了数字,据我所知,您将获得完整的 [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)]

【讨论】:

  • 谢谢,听起来不错。虽然代码对我来说很复杂,但我会尝试理解它,但我不太明白如何为其生成例如 10k 个数字
  • @tbone 请检查更新如何使用它生成 10k 数字。
  • 不应该是 rng.next 而不是 LCG.next 吗??
  • @tbone LCG.next 在哪里?在我的代码中看不到它
  • 我们不是必须在对象上调用这个方法吗? (rng.next () 而不是 LCG.next) 和 LCG.next () 我得到了这个错误:next () 缺少 1 个必需的位置参数:'self'。我在原始代码中遇到了这样的错误,但是在编辑时,我可能是错的
猜你喜欢
  • 2012-08-19
  • 2019-11-29
  • 1970-01-01
  • 2016-07-03
  • 1970-01-01
  • 1970-01-01
  • 2015-08-22
  • 2013-10-09
  • 1970-01-01
相关资源
最近更新 更多