如何在多个随机模块中使用随机种子?初始化随机种子应放在哪里?

4
每次我开发一些大型的功能时,需要多个模块协同工作来构建一个最终的功能,我就会想同样的问题:如果有多个模块需要使用随机函数,应该在哪里初始化随机种子呢?
如果我有一个需要随机数的类(例如,这个类通过自己实现的快速排序算法对输入数组进行初始化,因此需要用随机数选择枢轴),我通常会有一个私有的static bool isRandOn;变量,所以在开始随机选择枢轴之前,我会检查该变量,并执行srand(time(NULL));如果随机数还没有被打开。
如果我有很多实用函数在一个命名空间中,我会做类似的事情:我将这样的变量放在我的utils库的匿名命名空间中,并且做与类相同或相似的事情。
我面临的问题是当组合这些模块时。单独地,我知道每个模块不会设置种子超过一次。但是,我想能够使用各种各样的模块,我希望其他人能够独立使用一个或多个我的模块...
因此,如何处理需要多个随机种子的模块?在每个模块中设置种子?不设置种子,而是记录随机使用情况,并要求用户初始化种子以使用该模块?还是其他方式?

我不确定这是否算作答案:但如果你想要简单的东西,尽管有些破解,只需在第一次调用后修补srand,以便随后的调用不会产生影响。这是一个不需要库的解决方案,并且保证可以工作。 - Nowayz
@penelope:我不是专家,但可能会这样做。另外请注意,使用“time”函数是不安全的,所生成的随机数可以用于有趣的事情(在屏幕上随机弹出球等),但不应用于安全事务(如生成在线扑克游戏中玩家的手牌),因为人们可以提前猜测随机序列。 - Matthieu M.
@Nowayz:不必修补srand,可以编写一个包装器 :) - Matthieu M.
这更依赖于生成伪随机数的方法,而不是种子。我知道这是可能的,并且已经在攻击默认php随机生成器方面进行了演示,但在大多数情况下这并不现实。 - Nowayz
@Matthieu M. 我不需要用于安全目的,只是有一堆算法通常需要在开头使用类似枢轴的元素。 - penelope
显示剩余9条评论
4个回答

2

我建议使用Boost.Random,而不是依赖于在程序级别共享的某些全局状态。

Boost.Random有两个概念:

  • 引擎:生成随机数
  • 分布:将引擎的结果适应于特定分布(正常、泊松、高斯等)

然后,每个模块可能都有自己的引擎,甚至有几个引擎:没有一个给定的引擎必须在同一模块内的几个不同函数之间共享的具体原因。

最后一句话:无论你做什么,请确保有一种方法可以确定地设置种子,以便进行错误重现。多个引擎可能有助于错误重现(隔离各部分有助于)。


我知道关于调试的确定性种子。几年前我吃过苦头:) 这是一个好建议,但由于我正在寻找适用于各种情况的最佳实践,所以有什么建议可以在禁止使用诸如 Boost 之类的外部库时使用吗? (或者至少在团队中开发时不允许使用外部库?) - penelope
1
@penelope:重新开发相同的概念吗?Mersenne Twister 通常很容易实现,您可以直接使用其结果(类似于 rand() 会生成的内容)。Boost 中的分布只是糖衣外壳,使得当您真正想要在 [89, 99) 范围内获得整数时,语法更有意义,而不是像 rand() % 10 + 89 那样。当然,它们不会引入偏差(与我的快速演示相反)。 - Matthieu M.

1
您可以为随机数生成制作一个特殊的“模块”,并从应用程序的其他部分使用它。然后,当初始化随机数模块时,只需进行一次种子操作即可。

只有在以下情况下才有效:a)我是唯一使用和开发我的应用程序的人;b)没有其他人会使用我的任何单独模块;c)它不会与外部任何内容混合。我想知道最好的做法是什么,以尽可能广泛地适用。 - penelope

0
我想到的避免重复使用相同初始随机序列的最佳方法是在每个调用random()函数的模块中执行以下操作:
/* Global variable to remember if we already initialized the PRNG */
static bool seed_initialized = false;

/* Helper function to avoid having always the same sequence again and again */
static void
prng_init (unsigned int seed)
{
  if (!seed_initialized)
    {
      srandom (seed);
      seed_initialized = true;
    }
}

而且,每次在函数中使用random()时,你需要以类似以下的方式开始函数:

 /* Initializing PRNG with a 'reasonably strong' random seed for our purpose */
 prng_init (time (NULL) - getpid());

这样做可以确保:

  1. 您会至少在第一次通过时初始化PRNG;

  2. 您不会在模块内重新初始化随机序列超过一次。

希望这有所帮助!


0
@penelope 给出了正确的答案。在 rand() 后面有一些复杂的算法用于生成伪随机数序列。这就像一些函数 rand_func(prev_rand),它从前一个数字生成下一个伪随机数。第一次调用 srand(time(NULL)),它将 prev_rand 设置为假定 time(NULL) 是相当不确定的值。因此,您可以安全地多次调用 srand()(它设置)。

特殊问题是如果您需要可预测的伪随机序列:例如,srand(0) 等。但这似乎不是您的情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接