MT19937生成器在C++和NumPy生成不同的数字

e0uiprwp  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(125)

我试图在Python中重现一些涉及随机数生成的C代码。C代码使用MT19937生成器,如下所示:

#include <random>
#include <iostream>

int main() {
    std::mt19937 generator(1234);
    std::uniform_real_distribution<double> distribution(0.0, 1.0);

    for (int i = 0; i < 10; ++i) {
        std::cout << distribution(generator) << std::endl;
    }

    return 0;
}

字符串
Python版本是(NumPy 1.23.3)

import numpy as np

rng = np.random.Generator(np.random.MT19937(1234))
for _ in range(10):
    print(rng.random())


在这两种情况下,随机种子都被设置为1234。但在我的机器上(macOS 14.0 ARM),这两种情况产生不同的输出。

0.497664
0.817838
0.612112
0.77136
0.86067
0.150637
0.198519
0.815163
0.158815
0.116138


而Python代码输出

0.12038356302504949
0.4037014194964441
0.8777026256367374
0.9565788014497463
0.42646002242298486
0.28304326113156464
0.9009410688498408
0.830833142531224
0.6752899264264728
0.3977176012599666


为什么两个MT19937生成器产生不同的序列,尽管相同的种子?我如何(如果可能的话)使它们相同?

ngynwnxp

ngynwnxp1#

梅森扭曲发生器对你给予的任何种子都有一个定义的序列。还有一些测试值可以用来验证你使用的发生器是否符合要求。
另一方面,分布是 * 非 * 标准化的,在不同的实现中可能会产生不同的值。删除分布以比较生成器。
还要注意的是,std::mt19937是一个32位的生成器,(对我来说)numpy版本是32位还是64位的生成器并不明显。你可能想把std::mt19937_64和numpy实现进行比较-当然,仍然不涉及发行版。

cnh2zyt3

cnh2zyt32#

梅森扭转器引擎的C标准库版本与所有其他版本的不同之处在于如何从整数种子初始化状态。
你可以在C
中使用另一个mt19937库来匹配numpy方法(我认为这是大多数人实现mt19937的方式),或者你可以改变Python引擎的种子匹配方式:

import numpy as np
import numpy.random

WORD_SIZE = 32  # The template argument after the type
STATE_SIZE = 624  # The next template argument (Also `len(np.random.MT19937().state['state']['key'])`)
INITIALIZATION_MULTIPLIER = 1812433253  # The last template argument
DEFAULT_SEED = 5489  # A constant

def cpp_seed_mt19937(seed = DEFAULT_SEED):
    state = np.zeros(STATE_SIZE, dtype=np.uint32)
    state[0] = seed
    for j in range(1, STATE_SIZE):
        state[j] = INITIALIZATION_MULTIPLIER * (state[j-1] ^ (state[j-1] >> (WORD_SIZE - 2))) + j
    result = np.random.MT19937()
    result.state = {'bit_generator': 'MT19937', 'state': {'key': state, 'pos': STATE_SIZE - 1}}
    result.random_raw(1)  # Start at index "STATE_SIZE-1" and advance by 1 to advance past the generated state
    return result
    
engine = cpp_seed_mt19937(2)
print(*engine.random_raw(10), sep='\n')

个字符
这两种方法应该产生相同的输出:

1872583848
794921487
111352301
4000937544
2360782358
4070471979
1869695442
2081981515
1805465960
1376693511


现在,将这些32位数字转换为0和1之间的浮点数将有不同的结果,这取决于算法是如何实现的。你将不得不使用一些方法,这是标准化的给予相同的结果给定相同的随机数从梅森扭转。
(Also,std::uniform_real_distribution<double> distribution(0.0, 1.0);在不同的平台上不会给予相同的数字序列)
或者,您可以使用ctypes或其他方式调用一小部分C++代码,这些代码只为您生成随机数,其余代码可以使用Python。

相关问题