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

问题描述 投票:0回答:2

我正在尝试在 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)上产生不同的输出。 C++ 代码输出

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 生成器会产生不同的序列?我如何(如果可能)使它们相同?

python c++ numpy random random-seed
2个回答
1
投票

Mersenne Twister 生成器对于您提供的任何种子都有一个定义的序列。您还可以使用一些测试值来验证您使用的生成器是否符合要求。

另一方面,分布是不是标准化的,并且在不同的实现中可能会产生不同的值。删除分布以比较生成器。


0
投票

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

您可以使用另一个与 numpy 方法匹配的 C++

mt19937
库(我认为这是大多数人实现 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')
#include <random>
#include <iostream>

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

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

1872583848
794921487
111352301
4000937544
2360782358
4070471979
1869695442
2081981515
1805465960
1376693511

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

(此外,

std::uniform_real_distribution<double> distribution(0.0, 1.0);
不会在不同平台上为您提供相同的数字序列)

或者,您可以使用

ctypes
调用一小部分 C++ 代码,或者仅为您生成随机数,而您可以使用 Python 来获得其余代码。

© www.soinside.com 2019 - 2024. All rights reserved.