【问题标题】:C++ need a good technique for seeding rand() that does not use time()C++ 需要一种很好的技术来播种不使用 time() 的 rand()
【发布时间】:2018-11-12 17:00:36
【问题描述】:

我有一个启动许多客户端进程的 bash 脚本。这些是 AI 游戏玩家,我用来测试一个有很多玩家的游戏,大约有 400 个连接。

我遇到的问题是AI播放器使用

srand( time(nullptr) );

但是如果所有玩家几乎在同一时间开始,他们将经常收到相同的 time() 值,这意味着他们都在同一个 rand() 序列上。

测试过程的一部分是确保如果大量客户端几乎同时尝试连接,服务器可以处理它。

我曾考虑过使用类似的东西

srand( (int) this );

或类似的,依靠每个实例都有一个唯一的内存地址的想法。

还有其他更好的方法吗?

【问题讨论】:

标签: c++ random random-seed


【解决方案1】:

如果您想要可重现的结果,您可以使用随机数种子。这对于像地图生成这样的事情来说很方便,您希望地图是随机的,但您希望它是基于种子的可预测随机的。

在大多数情况下,您不希望这样,您实际上需要随机数,而最好的方法是通过标准库生成器函数:

#include <random>

std::random_device rd;
std::map<int, int> hist;
std::uniform_int_distribution<int> dist(0, 5);

int random_die_roll = dist(rd);

在这种情况下,不需要也不建议使用种子。 “随机设备”负责正确地播种 PRNG(伪随机数生成器)以确保不可预测的结果。

同样,不要使用srand(time(NULL)),因为它是一种非常古老、非常糟糕的初始化随机数的方法,而且它的可预测性很高。在现代计算机上,旋转一百万个可能的种子以找到匹配的输出是微不足道的。

【讨论】:

  • “当且仅当您想要可重复的结果时,您才使用随机数种子。” 这似乎有点强。虽然 std::random_device 相当快(至少在 Linux 上的 gcc 上),但您不会仅基于它运行 Monte-Carlo 模拟,而是使用它来播种 Mersenne twister。
  • 然后修正你的答案;那(速度)是使用种子prng的另一个原因。也许从随机设备播种,但播种。
  • std::random_device 可以是确定性的,如果实现没有实现非确定性的特性(或利用硬件特性)。在这种情况下,它实际上与使用种子相同。如果您确实需要不可预测性,则值得检查(例如阅读编译器/库的文档)。
  • 您谈到std::random_devicePRNG 播种,但您的代码中没有PRNG。此外,此代码很昂贵,甚至可能在设备收集更多熵时阻塞。我不认为这是一个很好的通用解决方案,即用std::random_device 播种PRNG 而不是直接使用它。
  • 谢谢大家...我喜欢 Mersenne 数字,并没有注意到 std 库的这一部分。非常有帮助!再次感谢您,它工作得很好。
【解决方案2】:

对伪随机生成器使用随机种子。

std::random_device 是昂贵的随机数据。 (缓慢而昂贵) 你用它来播种 prng 算法。 mt19937 是您将需要的最后一个 prng 算法。

如果需要,您可以选择通过分发来跟进它。即,如果您需要生成器提供的值以外的某个范围内的值。

std::random_device rd;
std::mt19937 generator(rd());

【讨论】:

  • 如果结果正确,即使它在多线程上下文中阻塞,也可以接受昂贵的(毫秒或微秒的数量级)
  • "如果结果正确"。考虑到需要一组拥有博士学位的数据科学家来区分实际随机数据样本和从 mt19937 生成的数据样本之间的区别,我想问你为什么不想要快速、可重复(如果需要)和正确。不使用它的唯一原因 (imo) 是如果您需要加密安全算法。
【解决方案3】:

现在rand()srand() 已经过时了。

普遍接受的方法是从std::random_device 播种一个伪随机数生成器。在提供非确定性随机源的平台上,std::random_device 需要使用它们来提供高质量的随机数。

但是,在收集足够的熵时,它可能会很慢甚至阻塞。因此,它通常只用于提供种子。

标准库提供的mersenne twister是一个高质量但高效的随机引擎:

inline
std::mt19937& random_generator()
{
    thread_local static std::mt19937 mt{std::random_device{}()};
    return mt;
}

template<typename Number>
Number random_number(Number from, Number to)
{
    static_assert(std::is_integral<Number>::value||std::is_floating_point<Number>::value,
        "Parameters must be integer or floating point numbers");

    using Distribution = typename std::conditional
    <
        std::is_integral<Number>::value,
        std::uniform_int_distribution<Number>,
        std::uniform_real_distribution<Number>
    >::type;

    thread_local static Distribution dist;

    return dist(random_generator(), typename Distribution::param_type{from, to});
}

【讨论】:

    猜你喜欢
    • 2011-04-11
    • 1970-01-01
    • 2012-10-24
    • 1970-01-01
    • 2020-08-25
    • 1970-01-01
    • 2015-09-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多