在 cpp 中使用 Seedsequence 复制随机数生成器的生成

Replicate the generation of random number generators using Seedsequence in cpp

提问人:Adhithi S 提问时间:5/31/2023 最后编辑:Adhithi S 更新时间:5/31/2023 访问量:82

问:

我正在将用 python 编写的 Gibb 采样器转换为 cpp。虽然代码工作正常,但存在可重复性问题。Python 代码能够为任何一组种子生成类似的输出,其中 cpp 代码结果在不同的运行中不一致。

以下代码片段用于在 Python 中为不同的迭代生成随机数:

ss=np.random.SeedSequence()
child_states = ss.spawn(n) #n is the number of iterations
for i in range(0,n):   
     inference(child_states[i]); 
def inference(seed):
     child_states = seed.spawn(len(Docs))
     for i in range(len(Docs)):
            seq(seed);
def seq(seed):
     rng = np.random.default_rng(seed)
     for i in range(0,V):
        z=rng.multinomial(................)

以下是使用 pcg64 对象在 c++ 中尝试的代码片段:

void inference(){
     pcg_extras::seed_seq_from<std::random_device> seedSource;
     pcg64 rng(seedSource);
     for(int i=0;i<m;i++)
         gsl_rng_set(r, rng());
         for(int j=0;j<V;j++)
             gsl_ran_multinomial(..................)
                
}

有没有办法在 CPP 中实现相同的伪随机数生成步骤?

修改更新: 对于少量样品,我能够解决重现性问题。所以我尝试的是

    std::vector<unsigned int> seed_values(10);
    std::generate(seed_values.begin(), seed_values.end(),        
    std::ref(rd));
    std::seed_seq seq(seed_values.begin(), seed_values.end()); 

生成一组 M 种子,并使用它来生成 M 个种子序列,如下所示

    std::seed_seq seq{seed};
    std::vector<std::uint64_t> seeds(m);
    seq.generate(seeds.begin(), seeds.end()); 

这适用于少量样本,但当样本数量很大时,似乎两个样本被分配了相似的随机数。种子位数会不会成为问题,因为在 Python 中种子位数至少为 128 位,而在 cpp 中最多为 64 位。

python 随机 种子

评论

1赞 Passer By 5/31/2023
“以下是在 cpp 中尝试的片段”不,不是。
0赞 Eldinur the Kolibri 5/31/2023
是的,可以在 C++ 中实现与 Python 中相同的伪随机数生成步骤。但是,需要注意的是,不同的随机数生成器在不同编程语言中的行为和实现细节可能略有不同。您能否展示您用 C++ 编写的内容以了解您在寻找的内容?
0赞 Passer By 5/31/2023
@πάνταῥεῖ 里面有一个,它也不是python。这不是标记的问题。std::random_device
0赞 Adhithi S 5/31/2023
@Eldinur 如果我遗漏了任何细节,我们深表歉意。我不确定如何实现 Seedsequence 部分。因此,我在 c++ 中使用了 pcg64 随机数生成器。
0赞 Eldinur the Kolibri 5/31/2023
@AdhithiS 如上所述,您的答案中没有C++代码。请修改它,以便我们可以看到您做了什么。(它不必工作,它不一定是完美的。但我想看到C++代码而不仅仅是python代码)

答:

1赞 Aconcagua 5/31/2023 #1

在 C++ 中,通常按如下方式初始化伪随机数生成器:

std::default_random_engine e((std::random_device())());

它产生一个随机数生成器,其中或多或少是随机选择的种子。

如果将其替换为固定种子,则无论编译程序运行多少次,都将收到相同的伪随机数序列;尝试例如

std::default_random_engine e(1012);
std::uniform_int_distribution<int> uid(0, 1000);
for(size_t i = 1; i <= 100; ++i)
{
    std::cout << std::setw(3) << uid(e) << (i % 10 ? ' ' : '\n');
}
std::cout.flush();

如果解析命令行参数,则可以动态交换序列,同时仍可控制何时接收与其他程序运行相同的序列。

C++ 带有相当多的预实现算法和发行版,请参阅 cpPreference – 如果其中有与 python 使用的算法和发行版相同的算法和发行版,那么您甚至有可能在 python 和 C++ 中为相同的种子生成相同的序列,但不能保证,实现细节的差异也可能阻止它。

上面使用 C++ 标准随机库(我宁愿推荐它而不是一些第三方库),因为我不熟悉 PCG 库。如果你更喜欢继续后者:那个使用种子生成器,我不知道它会多久调用一次,尽管在我看来,只假设一次似乎是合理的。如果您现在将代码片段替换为自定义代码片段,则始终在其中返回相同的预定义种子(您可以调整静态变量以通过命令行参数控制它),那么您可以再次可靠地重现相同的序列(但您需要自己测试......std::random_deviceoperator()

评论

0赞 Adhithi S 5/31/2023
非常感谢您提供 cppreference 链接。我能够使用 seed_seq 类中定义的 generate 函数重现结果:)
0赞 Adhithi S 5/31/2023
For a small number of samples, I am able to resolve the reproducibility issue. So what I tried is ''' std::vector<unsigned int> seed_values(10); std::generate(seed_values.begin(), seed_values.end(), std::ref(rd)); std::seed_seq seq(seed_values.begin(), seed_values.end()); ''' generate a set of m seeds and use that to generate m sequences of seeds like this ''' std::seed_seq seq{seed}; std::vector<std::uint64_t> seeds(m); seq.generate(seeds.begin(), seeds.end()); '''
0赞 Aconcagua 5/31/2023
@AdhithiS I'm not sure what you actually want to achieve... If indeed is a as I'd assume from its name then you won't ever be reproducible. You need identical seeds to reproduce identical sequences, thus you would initialise the seed generator with fixed values (or ones you parse from command line parameters) like rdstd::random_devicestd::seed_seq seq({12, 10, 51, 77, 54});