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

Zhe*_*Yue 3 c++ python random numpy random-seed

我正在尝试在 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;
}
Run Code Online (Sandbox Code Playgroud)

Python版本是(NumPy 1.23.3)

import numpy as np

rng = np.random.Generator(np.random.MT19937(1234))
for _ in range(10):
    print(rng.random())
Run Code Online (Sandbox Code Playgroud)

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

0.497664
0.817838
0.612112
0.77136
0.86067
0.150637
0.198519
0.815163
0.158815
0.116138
Run Code Online (Sandbox Code Playgroud)

而Python代码输出

0.12038356302504949
0.4037014194964441
0.8777026256367374
0.9565788014497463
0.42646002242298486
0.28304326113156464
0.9009410688498408
0.830833142531224
0.6752899264264728
0.3977176012599666
Run Code Online (Sandbox Code Playgroud)

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

Art*_*yer 5

mersennetwister 引擎的 C++ 标准库版本与所有其他版本的不同之处在于如何从整数种子初始化状态。

您可以使用与mt19937numpy 方法匹配的 C++ 中的另一个库(我认为这是大多数人实现 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')
Run Code Online (Sandbox Code Playgroud)
#include <random>
#include <iostream>

int main() {
    std::mt19937 e(2);
    for (int i = 0; i < 10; ++i)
        std::cout << e() << '\n';
}
Run Code Online (Sandbox Code Playgroud)

这两者应该产生相同的输出:

1872583848
794921487
111352301
4000937544
2360782358
4070471979
1869695442
2081981515
1805465960
1376693511
Run Code Online (Sandbox Code Playgroud)

现在,将这些 32 位数字转换为 0 到 1 之间的浮点数将会产生不同的结果,具体取决于算法的实现方式。您必须使用某种标准化的方法,才能在给定来自梅森扭曲器的相同随机数的情况下给出相同的结果。

(另外,std::uniform_real_distribution<double> distribution(0.0, 1.0);不会在不同平台上给你相同的数字序列)

或者,您可以使用一小部分 C++ 代码进行调用,ctypes或者以其他方式仅为您生成随机数,而其余代码可以使用 Python 编写。