4

我在几个地方使用随机数,并且通常在需要时构建一个随机数生成器。目前我使用 Marsaglia Xorshift 算法用当前系统时间播种它。现在我对这个策略有些怀疑:如果我使用多个生成器,那么生成器之间的数字的独立性(随机性)取决于种子(相同的种子相同的数字)。因为我使用时间(ns)作为种子,并且因为这个时间改变了这个工作,但我想知道是否只使用一个单一的生成器会不会更好,例如让它作为一个单例可用。这会增加随机数的质量吗?

编辑:不幸的是,c++11 还不是一个选项

编辑:更具体地说:我并不是说单例可以提高随机数质量,而是只使用一个生成器并播种的事实。否则,我必须确保不同生成器的种子彼此独立(随机)。极端的例子:我用完全相同的数字播种了两个生成器->它们之间没有随机性

4

5 回答 5

3

假设您有几个变量,每个变量都需要是随机的,独立于其他变量,并且会定期重新分配来自某个随机生成器的新随机值。这种情况经常发生在蒙特卡洛分析和游戏中(尽管游戏的严谨性远低于蒙特卡洛)。如果存在完美的随机数生成器,则可以使用它的单个实例化。将生成器中的第n伪随机数分配给变量x 1,下一个随机数分配给变量x 2,下一个分配给x 3,依此类推,最终回到变量x 1在下一个周期。大约。这里有一个问题:当使用这种方式时,太多的 PRNG 未能通过独立性测试,有些甚至无法通过单个序列的随机性测试。

我的方法是使用单个 PRNG 生成器作为一组N个自包含 PRNG 实例的种子生成器。后面这些 PRNG 的每个实例都提供一个变量。通过自包含,我的意思是 PRNG 是一个对象,在实例成员中而不是在静态成员或全局变量中维护状态。种子生成器甚至不需要与其他N来自同一家族PRNG。它只需要在多个线程同时尝试使用种子生成器的情况下是可重入的。但是,在我的使用中,我发现最好在线程开始之前设置 PRNG,以保证可重复性。那是一次运行,一次执行。蒙特卡洛技术通常需要数千次执行,也许更多,也许更多。对于 Monte Carlo,可重复性至关重要。所以还需要一个随机种子生成器。这个种子生成器用于生成变量的N个生成器。

可重复性很重要,至少在蒙特卡洛世界是这样。假设一次长蒙特卡罗模拟的第 10234 次运行导致一些严重的失败。很高兴看到世界上发生了什么。这可能是统计上的侥幸,也可能是个问题。问题在于,在典型的 MC 设置中,只记录了最少的数据,足以计算统计数据。要查看运行编号 10234 中发生了什么,需要重复该特定情况,但现在记录所有内容。

于 2012-10-08T10:47:50.987 回答
1

每当客户端相互关联并且代码需要“独立”随机数时,您应该使用随机生成器类的相同实例。

当客户端不相互依赖时,您可以使用随机生成器类的不同对象,并且它们是否收到相同的数字并不重要。

请注意,对于测试和调试而言,能够再次创建相同的随机数序列非常有用。因此,您不应该“随机播种”太多。

于 2012-10-08T09:50:58.627 回答
0

我不认为它增加了随机性,但它减少了每次你想使用随机生成器时创建对象所需的内存。如果此生成器没有任何特定于实例的设置,您可以创建一个单例。

于 2012-10-08T08:46:53.337 回答
0

因为我使用时间(ns)作为种子,并且因为这个时间改变了这个工作,但我想知道是否只使用一个单一的生成器会不会更好,例如让它作为一个单例可用。

当单例不是反模式时,这是一个很好的例子。您还可以使用某种控制反转

这会增加随机数的质量吗?

不会。质量取决于生成随机数的算法。你如何使用它是无关紧要的(假设它使用正确)。

对于您的编辑:您可以创建某种容器来保存您的 RNG 类的对象(或使用现有容器)。像这样的东西:

std::vector< Rng > & RngSingleton()
{
  static std::vector< Rng > allRngs( 2 );
  return allRngs;
}

struct Rng
{
  void SetSeed( const int seen );
  int GenerateNumber() const;
//...
};

// ...
RngSingleton().at(0).SetSeed( 55 );
RngSingleton().at(1).SetSeed( 55 );
//...
const auto value1 = RngSingleton().at(0).GenerateNumber;
const auto value2 = RngSingleton().at(1).GenerateNumber;
于 2012-10-08T09:02:25.300 回答
-1

工厂模式来拯救。客户端永远不必担心其依赖项的实例化规则。它允许交换创建方法。反过来,如果你决定使用不同的算法,你可以交换生成器类,客户端不需要重构。 http://www.oodesign.com/factory-pattern.html

- 编辑

添加了伪代码(对不起,它不是 c++,自从我上次工作以来,它是很久以前的事情了)

interface PRNG{
    function generateRandomNumber():Number;
}

interface Seeder{
    function getSeed() : Number;
}

interface PRNGFactory{
    function createPRNG():PRNG;
}

class MarsagliaPRNG implements PRNG{
    constructor( seed : Number ){
        //store seed
    }

    function generateRandomNumber() : Number{
        //do your magic
    }
}

class SingletonMarsagliaPRNGFactory implements PRNGFactory{
    var seeder : Seeder;
    static var prng : PRNG;
    function createPRNG() : PRNG{
        return prng ||= new MarsagliaPRNG( seeder.getSeed() );
    }
}

class TimeSeeder implements Seeder{
    function getSeed():Number{
        return now();
    }
}

//usage:
seeder : Seeder = new TimeSeeder();
prngFactory : PRNGFactory = new SingletonMarsagliaPRNGFactory();

clientA.prng = prngFactory.createPRNG();
clientB.prng = prngFactory.createPRNG();
//both clients got the same instance.

现在最大的优势是,如果您想要/需要更改任何实现细节,客户端无需更改任何内容。您可以更改播种方法、RNG 算法和实例化规则,而无需在任何地方接触任何客户端。

于 2012-10-08T10:48:02.267 回答