【发布时间】:2016-07-05 09:56:19
【问题描述】:
我正在研究程序生成,以及它与随机生成之间的区别。我知道不同之处在于它是确定性的,它基于特定的种子值,就像所有随机引擎一样。
所以在 C++11 中,我知道要获得“最佳”随机序列,应该使用 std::seed_seq,它不需要加密安全,所以 std::mt19937 就可以了。
在这种情况下,我想要世界上物体的一堆位置,这样我就可以生成一个关卡,然后我想给我的朋友发短信这个全新关卡的种子,因为它真的很酷。但事实上,输入189151022 140947902 1454660100 853918093 3243866855 真的很烦人。那么作为开发人员,我可以做些什么来确保随机性得到保留并具有更多的类型能力?
我认为将值散列为字符串,然后将其反转,(但后来我记得散列的意义)或者只是使用散列本身,但作为种子会更糟吗?或者它甚至有什么关系,我可以只使用“lol”作为我的种子,让它和一个兆字节长的完美随机数一样好吗?
这是我为帮助我更好地理解它而制作的快速示例。
#include <iostream>
#include <random>
#include <array>
using std::cout;
using std::endl;
using std::array;
struct pos
{
float x, y;
pos(float x, float y) : x(x), y(y) {}
void print()
{
cout << "(" << x << " " << y << ")" << endl;
}
};
int main()
{
//Get seed
std::random_device rd;
array<unsigned long, 5> seed = { rd(), rd(), rd(), rd(), rd() };
//Seed generator
std::mt19937 generator;
generator.seed(std::seed_seq(seed.begin(), seed.end()));
//Setup distribution
std::uniform_real_distribution<float> distribution(0, 100);
//Generate the world (or whatever)
pos a = pos(distribution(generator), distribution(generator));
pos b = pos(distribution(generator), distribution(generator));
pos c = pos(distribution(generator), distribution(generator));
//And many, many more calls to get values from the generator
a.print();
b.print();
c.print();
//For when I want the same world back
cout << "Seed: ";
for (unsigned long s : seed)
{
cout << s << " ";
}
cout << endl;
}
要清楚,我要问的问题是:
我应该在游戏环境中的程序生成器中使用什么作为种子,以这种方式进行操作有什么好处和坏处?
【问题讨论】:
-
在游戏中通常没有必要使用“花哨”的随机数。您是否尝试过使用普通的 32 位种子(四个十六进制数字,非常可文本)并得出结论认为它不够好? (最适合您的游戏的“最佳”RNG 是最适合您的,不一定是具有“最佳随机性”的那种。)
-
@molbdnilo 我不知道,基本上。因此问题。我仍然对随机数生成的工作原理以及是否会遇到诸如仅使用短种子的池耗尽之类的问题感到困惑。
-
附带说明:您对
distribution的两次调用以未指定的顺序发生,因此位置可能因编译器甚至编译器设置而异。如果您想要可靠的位置,请在将它们传递给pos之前计算它们的值。 -
@molbdnilo 很公平,我会记住这一点,但代码更多的是作为示例而不是我使用的东西。但除此之外,我很乐意接受您第一条评论的引用版本作为答案。
-
如果您不知道,“可能工作的最简单的事情”是一个很好的起点。只需抽象出 RNG 细节,以便您可以轻松地交换实际的生成器。审美和享受比数学严谨更重要。 (我没有发布任何答案,因为我认为除了“从简单开始,尝试一些东西,看看会发生什么,然后从那里得到它”之外,没有其他答案了。)
标签: c++ c++11 random procedural-generation