随机数生成器的问题

时间:2016-09-29 06:45:17

标签: c++ codeblocks

我正在关注此video tutorial (C++/Game Tutorial 8: Random Number Generator)。我编写了程序,但每次都得到相同的输出。

#include <iostream>
#include <random>
#include <string>
#include <ctime>

using namespace std;

int main()
{

    default_random_engine randomGen(time(0));
    uniform_int_distribution<int> diceRoll(1, 6);

    cout << diceRoll(randomGen) << endl;
    return 0;
}

我使用Code :: Blocks。我在Visual Studio中试过它,它运行正常。这是否意味着它的Code :: Blocks问题?

2 个答案:

答案 0 :(得分:4)

TL; DR default_random_engine不会给您任何保证。如果您想要数字随机,请改用mt19937_64。如果要生成安全相关的随机性(密钥,IV,盐,密码......),请查找从random_device播种的CSPRNG或使用random_device。请参阅下文,了解default_random_engine的{​​{1}}行为的有趣分析。

g++是实现定义的

正如评论中所提到的,default_random_engine具有实现定义的行为,您根本不应该使用它。实现定义意味着实现可以免费使用(in)着名的XKCD rng

default_random_engine

class default_random_engine { public: using result_type = uint32_t; result_type min() const { return 1; } result_type max() const { return 6; } default_random_engine(int) {}; // we don’t care about the argument result_type operator()() const { return 4; // chosen by fair dice roll // guaranteed to be random } }; 对于彼此接近的种子不起作用

然而,实际上似乎只是default_random_engine(在GNU libstdc ++中使用)与default_random_engine当前产生的种子和/或用{time(0)采样的分辨率高度偏向。 1}}。尝试在同一程序中一次生成几个随机数。您会找到以下输出:uniform_int_distribution<int>(1, 6)6 6 6 4 6 2 2 3,....简而言之,6 3 2 1 4 6 3 3没有任何保证,并且可能是任何目的的非常糟糕的RNG(显然,甚至教授随机API,因为它在生成单个时不会出现随机用UNIX纪元时间戳播种的diceroll!)。

所以我跑了数字。通过一个小程序(见下文),我计算了default_random_enginedefault_random_engine生成的第一个骰子卷数的概率分布为6,绘制在种子范围内。这是使用g ++(Debian 6.1.1-11)6.1.1 20160802和libstdc ++ 6.1.1-11。

Plot of the probability to get a 6 in the first draw over seed

该图显示了在X轴上使用种子初始化相应的随机引擎后,在第一次绘制中获得6 mt19937_64 的概率。此外,当前的unix时间绘制为垂直黑线(仅在图表的顶部范围内看到)。如您所见,我们目前处于整数分布非常可能(100%)的范围内,使用std::uniform_int_distribution<int> (1,6)输出6作为第一个数字。相比之下default_random_engine非常接近预期的概率。直方图的bin大小为10000(或者自unix-epoch以来大约2.8小时的秒数。)

此外,在整个测试范围(0..4e9-1)中,mt19937_64实际上产生6作为第一个数字,概率大约为⅙。处理彼此“接近”(±1000)的种子真是太糟糕了。

结论

因此,在某种程度上,default_random_engine 的罪魁祸首,因为它正在缓慢变化。而且,(使用的标准库)Code :: Blocks是罪魁祸首,因为他们的time(0)很糟糕。适当的RNG不应该偏向于如此大范围的种子的特定结果。只需使用适当的RNG,并且不要试图使用default_random_engine很好地工作,这不能保证在编译器和标准库之间可移植。

如果您需要随机数用于数字目的(例如游戏物理模拟),请使用C ++ 11标准提供的数字声音随机数引擎之一(例如提到的mt19937_64)。

如果您需要任何与安全相关的随机数(生成密码,盐,IV用于加密,密钥等),请使用mt19937_64,但使用使用default_random_engine播种的CSPRNG或直接std::random_device

另一个注意事项,因为本教程面向游戏开发:一旦网络发挥作用,您实际上可能需要独立于平台的行为。在这种情况下,选择具有特定参数的特定RNG引擎对于在客户端以及客户端和服务器之间实现可重现的结果至关重要。

来源

std::random_device

#include <array> #include <cstdint> #include <iostream> #include <random> #include <ctime> void search() { static constexpr uint32_t SEARCH_MAX = 4000000000; static constexpr uint32_t SEARCH_BLOCKS = 8; static constexpr uint32_t SEARCH_BIN_SIZE = 10000; static constexpr uint32_t SEARCH_BINS = SEARCH_MAX / SEARCH_BIN_SIZE; static constexpr uint32_t SEARCH_STEP = 1; static constexpr uint32_t OUTPUT_STEP = 1000000; std::vector<uint32_t> histogram(SEARCH_BINS); for (uint32_t &member: histogram) member = 0; std::uniform_int_distribution<int> diceRoll(1, 6); static constexpr uint32_t START = BLOCK * (SEARCH_MAX/SEARCH_BLOCKS); static constexpr uint32_t END = START + SEARCH_MAX/SEARCH_BLOCKS; for (uint32_t i = START; i < END; i+=SEARCH_STEP) { rng_engine_to_use foo(i); int roll = diceRoll(foo); if (roll == 6) { histogram[i/SEARCH_BIN_SIZE] += 1; } if (i % OUTPUT_STEP == 0) { std::cerr << ((float)i / SEARCH_MAX * 100) << "% \r" << std::flush; } } for (uint32_t i = START / SEARCH_BIN_SIZE; i < END/SEARCH_BIN_SIZE; ++i) { std::cout << i*SEARCH_BIN_SIZE << " " << histogram[i] << std::endl; } } int main() { search(); } d BLOCK为0..7,为我的每个CPU内核构建一个版本,因为mt19937_64需要相当长的时间来生成所有这些数字(我之后才意识到该情节在整个范围内绘制时是不可读的,但是meh。)。我将#define替换为rng_engine_to_usemt19937_64,并绘制了生成的直方图(如上所示)。

答案 1 :(得分:0)

正如this post中所指出的,在PO遵循相同的Youtube教程的主题中,可能是time(0)导致此问题。海报建议使用std::random_device作为种子。

#include <iostream>
#include <string>
#include <chrono>
#include <random>
#include <ctime>

using namespace std;

int main() {
    default_random_engine randomGenerator(std::random_device{}());
    // OR:
    // default_random_engine randomGenerator(
    //  (unsigned) chrono::system_clock::now().time_since_epoch().count());

    uniform_int_distribution<int> diceRoll(1, 6);

    cout << "You rolled a " << diceRoll(randomGenerator) << endl;

    return 0;
}