4

我目前正在运行一个带有 8 个以上管道(线程)的多线程模拟应用程序。这些管道运行非常复杂的代码,该代码依赖于种子生成的随机序列。然后将序列归结为单个 0/1。

在将种子从主线程传递到处理管道后,我希望这种“随机处理”是 100% 确定性的。所以,我可以在第二次运行中复制结果。

所以,例如:(我有这个编码并且它有效)

Pipe 1 -> Seed: 123 -> Result: 0
Pipe 2 -> Seed: 123 -> Result: 0
Pipe 3 -> Seed: 589 -> Result: 1

当我需要运行 100M 或更多这些进程然后平均结果时,就会出现问题。可能是 100M 中只有 1 个为 1,其余为 0。很明显,我无法对 100M 随机值采样 32 位种子馈送到srand().

是否可以在 VS2010 中使用 64 位种子播种到 srand(),或者使用等效的方法?

rand() 是在 2^32 之后重复还是不重复(有一些内部隐藏状态)?

谢谢

4

5 回答 5

3

您可以使用 C++11 的随机工具来生成给定大小和种子大小的随机数,尽管该过程有点过于复杂,无法在此总结。

例如,您可以构造std::mersenne_twister<uint64_t, ...>一个 64 位整数并为其播种,然后获取指定分布内的随机数,这似乎正是您要寻找的。

于 2013-11-01T20:13:57.907 回答
3

一个简单的 64 位 LCG 应该可以满足您的需求。LCG 的第 n 位(从最低有效位开始计算为第 1 位)最多有周期(并且,如果参数选择正确,则正好)2^n,因此如果不需要它们,请避免使用低位,并且/或在输出上使用回火功能。示例实现可以在我对另一个问题的回答中找到:

https://stackoverflow.com/a/19083740/379897

并转发:

static uint32_t temper(uint32_t x)
{
    x ^= x>>11;
    x ^= x<<7 & 0x9D2C5680;
    x ^= x<<15 & 0xEFC60000;
    x ^= x>>18;
    return x;
}
uint32_t lcg64_temper(uint64_t *seed)
{
    *seed = 6364136223846793005ULL * *seed + 1;
    return temper(*seed >> 32);
}
于 2013-11-01T20:57:32.597 回答
2

您可以使用 XOR SHIFT 伪随机数生成器

它速度快而且效果很好——这是我的实现类的实际生成部分。我在关于伪随机数生成器的维基百科搜索中找到了有关此算法的信息...

uint64_t XRS_64::generate(void)
{
    seed ^= seed >> 12; // a
    seed ^= seed << 25; // b
    seed ^= seed >> 27; // c
    return seed  * UINT64_C(2685821657736338717);
}

它速度很快,对于初始化,您可以在构造函数中执行此操作

XRS_64::XRS_64()
{
    seed = 6394358446697381921;
}

seed 是一个 unsigned int 64 位变量,它在类中声明。

class XRS_64
{
public:
    XRS_64();
    ~XRS_64();
    void init(uint64_t newseed);
    uint64_t generate();

private :
    uint64_t seed; /* The state must be seeded with a nonzero value. */
};
于 2016-09-24T11:03:39.573 回答
0

我不能回答你的问题,但是如果你发现你不能做你想做的事,你可以实现你自己的以 a作为种子的伪随机算法生成器。uint64_t如果您想要一些更严肃的生成器(例如,用于加密目的),有更好的算法可以用于此目的,但 LCG 是我见过的最容易实现的算法。

编辑

实际上,您不能rand()为该功能使用 64 位种子。你必须自己去。在这个Wikipedia 表中,MMIX Donald Knuth 使用了一些参数来实现它。请注意,根据您使用的参数,您的随机数生成器周期的值将比 2^64 小得多,并且由于乘法,您可能需要一个大数库来处理数学运算。

于 2013-11-01T20:03:09.517 回答
0

我的建议是您直接控制该过程并设置您自己的高质量随机数生成器。这里的答案都没有经过适当的测试或验证——这是一个需要考虑的重要标准。

即使在 16 位和 32 位机器上,只要并行运行其中的几个,就可以长时间制作高质量的随机数生成器 - 但需要满足某些先决条件。这在此处进行了更深入的描述

PL'Ecuyer,“高效便携的组合随机数发生器”,CACM 31(6),1988 年 6 月,742-751。

还提供了测试和验证结果。文章的可访问版本可以在网上找到。

对于 32 位实现,建议采用 M₀ = 1 + 2×3×7×631×81031 (= 2³¹ - 85) 和 M₁ = 1 + 2×19×31×1019×1789 (= 2³¹ - 249) 生成周期为 (M₀ - 1)(M₁ - 1)/2 ≡ 2×3×7×19×31×631×1019×1789×81031 ≡ 2⁶¹ - 360777242114 的随机数生成器。他们还发布了一个建议对于 16 位 CPU。

种子被更新为 (S₀, S₁) ← ((A₀×S₀) mod M₀, (A₁×S₁) mod M₁),并且可以由此产生一个 32 位值 S₀ - S₁,并将结果向上调整为M₀ - 如果 S₀ ≤ S₁,则为 1。如果 (S₀, S₁) 初始化为区间 [0,M₀)×[0,M₁) 中的整数值,则每次更新时它都保持在该区间内。您必须修改输出值以满足您的需要,因为它们的版本专门用于产生严格的积极结果;并且没有 0。

前提条件是 (M₀ - 1)/2 和 (M₁ - 1)/2 互质,且 A₀² < M₀,A₁² < M₁;根据他们的分析,推荐值 (A₀, A₁) = (40014, 40692)。还列出了允许使用 16 位或 32 位算术完成所有计算的优化例程。

对于 32 位,更新完成为 (S₀, S₁) ← (A₀×(S₀ - K₀×Q₀) - K₀×R₀, A₁×(S₁ - K₁×Q₁) - K₁×R₁),其中任何 S₀ < 0 或S₁ < 0 结果分别向上调整为 S₀ + M₀ 或 S₁ + M₁;其中(K₀,K₁)=(S₀ div Q₀,S₁ div Q₁),(Q₀,Q₁)=(M₀ div A₀,M₁ div A₁)和(R₀,R₁)=(M₀ mod A₀,M₁ mod A₁)。

于 2021-10-23T20:23:33.657 回答