Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python RNG技术的可移植性和再现性_Python_Algorithm_Math_Random - Fatal编程技术网

Python RNG技术的可移植性和再现性

Python RNG技术的可移植性和再现性,python,algorithm,math,random,Python,Algorithm,Math,Random,我可以使用两种方法中的一种来创建一个伪随机数序列,该序列具有两个重要特征:(1)它在不同的机器上是可复制的,(2)该序列在所有数字发出之前不会重复范围内的数字 我的问题是-这两种方法在可移植性(OS、Python版本等)方面是否存在潜在问题?例如,有人知道当XXX为真时,我是否会在一个系统上得到一组结果,但在另一个系统上得到一组不同的结果吗? 我并不是在征求关于使用哪种方法的建议,只是在Z为真时我应该注意Y系统上的X 我试过几个版本的Linux,都是64位的,它们看起来是一致的,但我无法轻松访问

我可以使用两种方法中的一种来创建一个伪随机数序列,该序列具有两个重要特征:(1)它在不同的机器上是可复制的,(2)该序列在所有数字发出之前不会重复范围内的数字

我的问题是-这两种方法在可移植性(OS、Python版本等)方面是否存在潜在问题?例如,有人知道当XXX为真时,我是否会在一个系统上得到一组结果,但在另一个系统上得到一组不同的结果吗?

我并不是在征求关于使用哪种方法的建议,只是在Z为真时我应该注意Y系统上的X

我试过几个版本的Linux,都是64位的,它们看起来是一致的,但我无法轻松访问Windows或32位版本

请注意,它们彼此产生的范围并不相同,但出于我的目的,这是可以的。这些数字在人眼看来必须是随机的

方法1
使用本机Python库函数从一个范围生成一个随机样本。如果我使用较大的范围(10米或更大),速度会很慢,但相对较小的范围也可以,并且对于没有数学学位的人来说更容易理解:

import random
random.seed(5)
x = random.sample(range(10000,99999),89999)
for i in range(10):
   print(x[i])
方法2
使用的算法不是来自Python库:
()
即使在大范围内,速度也非常快,但更难理解,因此发现以下方面的潜在问题:

def lcg(模数、a、c、种子): 尽管如此: 种子=(a*种子+c)%模量 出籽 m=10000019 c=int(m/2) a=5653 s=a g=lcg(m、a、c、s) 对于范围(10)内的uu: 打印(下一页(g))
注:我对其他选择非常开放;最初的问题在这里被问到:

LCG很好。如果您想让LCG更容易理解,您可以递归地实现它,而不是迭代地实现它,以突出显示它所基于的递归公式。只有在你不太担心复杂性的情况下才去做


否则,我认为方法2对于PRNG来说已经足够清晰了。

一个算法(特别是伪随机数生成器)可以在多台计算机上实现。最值得注意的方法是,如果算法依赖于浮点数(几乎总是有限精度)和浮点舍入

一些编程语言比其他语言更容易出现可移植性问题。例如,不像Python,由于-< /P>,C或C++中可能会出现不一致的结果。
  • 有符号整数溢出的未定义行为,或
  • 某些数据类型的长度,尤其是
    int
    long
    ,在不同的编译器中是如何定义的

我不知道方法2中的Python代码会以任何方式在计算机上传递不一致的结果。另一方面,方法1是否可以这样做取决于
random.sample
在您关心的所有Python版本和所有计算机上的实现方式是否相同。

大多数可移植版本IMO将是周期等于机器自然字长的LCG。它对模块使用寄存器溢出,这使它更快。您必须使用NumPy数据类型来实现这一点,这里是简单的代码,常量a,c取自表4

Linux x64和Windows x64以及OS X VM的工作原理完全相同

关于再现性,唯一好的方法是存储前两个数字,并在应用程序初始化阶段将其与LCG输出进行比较-如果它们正常,则继续


我喜欢LCG的另一个特性是它能够在log2(N)时间内跳到前面,其中N是跳过的次数。我可以为您提供代码来实现这一点。使用提前跳转可以确保并行独立随机流的序列不重叠

更新

这里是我的C代码到Python/NumPy的翻译,看起来很有效。它可以在对数时间内向前和向后跳跃

import numpy as np

class LCG(object):

    UZERO: np.uint64 = np.uint64(0)
    UONE : np.uint64 = np.uint64(1)

    def __init__(self, seed: np.uint64, a: np.uint64, c: np.uint64) -> None:
        self._seed: np.uint64 = np.uint64(seed)
        self._a   : np.uint64 = np.uint64(a)
        self._c   : np.uint64 = np.uint64(c)

    def next(self) -> np.uint64:
        self._seed = self._a * self._seed + self._c
        return self._seed

    def seed(self) -> np.uint64:
        return self._seed

    def set_seed(self, seed: np.uint64) -> np.uint64:
        self._seed = seed

    def skip(self, ns: np.int64) -> 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.uint64 = np.uint64(ns)

        a: np.uint64 = self._a
        c: np.uint64 = self._c

        a_next: np.uint64 = LCG.UONE
        c_next: np.uint64 = 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.uint64(2862933555777941757)
c = np.uint64(1)
seed = np.uint64(1)

rng64 = LCG(seed, a, c) # initialization

print(rng64.next())
print(rng64.next())
print(rng64.next())

rng64.skip(-3) # back by 3
print(rng64.next())
print(rng64.next())
print(rng64.next())

rng64.skip(-3) # back by 3
rng64.skip(2) # forward by 2
print(rng64.next())
无论如何,LCG RNG的总结:

  • 有了好的常数(参见L'Ecuyer论文的参考资料),它将覆盖整个[0…264)范围而不会重复自身。基本上完美的[0…264)->[0…264)映射,您可以设置 0,1,2,3,…作为输入,并获得整个范围的输出
  • 它是可逆的,您可以恢复上一个种子,所以映射实际上是 双射[0…264)[0…264]。有关详细信息,请参阅
  • 它具有对数向前和向后跳跃,因此不存在查找问题 适合并行计算的时间间隔-从单个种子开始,然后下一个线程将被跳过(种子,264/N),下一个线程跳过(种子,264/N*2)等等。保证不会重叠
  • 它简单快速,但不是一个非常高质量的RNG

  • 但是它们都是可移植的吗?我会编辑这个问题来更好地定义它,但我指的是python版本、OS类型和版本等。我不知道随机库是如何工作的(它是基于OS原语的吗?它基于什么PRNG?).Solution 2适用于任何当前的python版本2.7和3,以及任何具有python的操作系统。@Dukeling-RNG只需符合人性(即看起来像是随机的)只要它不重复,并且可以在机器之间复制。我想问题是模糊的,我想便携性问题可能包括如何在引擎盖下解决问题,这可能会使一台机器上的种子表现出与另一台不同的行为。我倾向于发现其他更有经验的人只是“知道”这些东西,所以我认为值得一问。我当然会尽可能多地测试。只要使用一个特定的RNG。如果你的LCG满足你的需要,那么就使用它,没有理由为此烦恼。python和几乎所有其他编程语言都是关于可移植性的,如果它们的行为在不同的机器之间发生变化,它们就没有多大用处。“我可以为您提供这样做的代码”-是的,请!我发现整个主题很吸引人。有没有办法限制您的示例中的最大位数长度?我正在尽可能将其限制为7位数。@DavidW
    import numpy as np
    
    class LCG(object):
    
        UZERO: np.uint64 = np.uint64(0)
        UONE : np.uint64 = np.uint64(1)
    
        def __init__(self, seed: np.uint64, a: np.uint64, c: np.uint64) -> None:
            self._seed: np.uint64 = np.uint64(seed)
            self._a   : np.uint64 = np.uint64(a)
            self._c   : np.uint64 = np.uint64(c)
    
        def next(self) -> np.uint64:
            self._seed = self._a * self._seed + self._c
            return self._seed
    
        def seed(self) -> np.uint64:
            return self._seed
    
        def set_seed(self, seed: np.uint64) -> np.uint64:
            self._seed = seed
    
        def skip(self, ns: np.int64) -> 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.uint64 = np.uint64(ns)
    
            a: np.uint64 = self._a
            c: np.uint64 = self._c
    
            a_next: np.uint64 = LCG.UONE
            c_next: np.uint64 = 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.uint64(2862933555777941757)
    c = np.uint64(1)
    seed = np.uint64(1)
    
    rng64 = LCG(seed, a, c) # initialization
    
    print(rng64.next())
    print(rng64.next())
    print(rng64.next())
    
    rng64.skip(-3) # back by 3
    print(rng64.next())
    print(rng64.next())
    print(rng64.next())
    
    rng64.skip(-3) # back by 3
    rng64.skip(2) # forward by 2
    print(rng64.next())