【问题标题】:Reseeding std::rand / c++11 <random>重新播种 std::rand / c++11 <random>
【发布时间】:2016-07-03 17:41:40
【问题描述】:

我正在尝试实现一个简单的噪声函数,它接受两个整数并根据种子和两个参数的组合返回一个随机浮点数。

使用std::mt19937 效果很好,但由于某种原因,当我尝试将srandrand() 一起使用时,我得到重复的数字..

注意:在循环中使用 c++11 seed 成员函数非常非常慢。

这里有两个使用两种方法的地形(具有相同的重新播种数):

c++11 随机:

std::random_device rd;
std::mt19937 g{ rd() };
std::uniform_real_distribution<float> gen{ -1.0, 1.0 };

float getNoise(int x, int z) {
     g.seed(x * 523234 + z * 128354 + seed);
     return gen(g);
}

c 随机:

float getNoise(int x, int z) {
        std::srand(x * 523234 + z * 128354 + seed);
        return static_cast<float>(std::rand()) / RAND_MAX * 2.0f - 1.0f;
}

问题:

  1. 是否有更快的方法来重新植入 c++11 伪随机数?
  2. 为什么srand 没有按预期工作?

提前致谢。

编辑: 好的抱歉不清楚,而且,我知道也许我错了,但让我再次尝试解释一下,我使用重新播种,因为我在迭代时使用相同的 x 和 z 坐标(不是相同的迭代)。

如果我删除重新播种,我将得到以下结果:

【问题讨论】:

  • 您正在根据xzseed 重新播种...如果这些值相同,您的种子也会...所以您会得到来自rand() 的相同号码。
  • "我得到重复的数字.. [暗示那些是不需要的]" vs "我不想得到一个完全随机的数字" => 选择一个。你意识到这些是相互排斥的,对吧?如果你想要一些半随机的东西,那么定义一个合适的分布。不要只是试图用你想到的第一个 hack 来让 RNG 变得“不那么随意”。
  • 也许如果您不想要完全随机分布,您应该设置限制并应用随机数来获得变化。
  • 我可能弄错了,但在我看来,您似乎找到了一种减少一台发电机的“随机性”的万能方法,您想知道为什么您的万能方法对不同的发电机。我认为你将不得不重新设计你的算法以依赖实际的随机性,而不是试图将适量的秩序强加给混乱。
  • @NaorHadar 是的,您需要使用与您想要查看的结果类型相匹配的分布和后续处理,同时仍提供足够的随机性以引起兴趣。在不同的 stdlibs/runtimes 和诸如此类的东西之间进行重新播种等不太可能是可靠的,因此您需要找出一种方法。有利的一面是,您可以更好地控制您将获得的输出类型。

标签: c++ c c++11 random


【解决方案1】:

我正在尝试实现一个简单的噪声函数,它接受两个整数并根据种子和两个参数的组合返回一个随机浮点数。

请不要说我不应该补种,我要故意补种。

你故意打破它并问我们为什么它被打破了,但我们不能在房间里提到大猩猩。

不要重新播种。

[编辑]
好的,根据评论请求,这是一个答案:

1) 不,没有更快的方法来重新播种 PRNG,无论如何您都不应该这样做。正确地,您应该通过丢弃几千个值来播种并“预热” PRNG。

2) rand()(即使您不相信,您使用的任何其他 PRNG)不起作用的原因是因为您的 getNoise() 函数不正确。

您的第三张图片是正确的。这是您应该从简单地返回一个固定的随机值所期望的结果。

您试图通过弄乱种子来调节它,并且由于您第一次尝试时明显的视觉效果,因此得出结论认为这是正确的方法。然而,真正发生的是你只是削弱了 PRNG 并看到了结果。 (rand() 的尝试更清楚,因为它的种子更粗略地定义了结果序列,它本身的周期比 Mersenne Twister 更小​​。)

(试图通过倾斜(x,z)坐标来修改它也是一个红鲱鱼。它不会影响输出的实际随机性。)

TL;DR
你做错了。

如果你想生成地形图,你应该google一下分形地形生成。事实上,这里有一个不错的链接:http://www.gameprogrammer.com/fractal.html

您会发现它需要做更多的工作,但方法非常简单,您可以很容易地调整它们来修改您的结果。

希望这会有所帮助。

【讨论】:

  • 关于地形的非常有趣的文章。呵呵,我不知道“热身”是一回事。本文档详细说明:www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf +1 进行编辑!
  • 这是一篇相当不错的文章。我在第一次通过时唯一会选择的是(坏的)建议在你的种子上使用散列。哈希不属于随机数的播种或产生的任何地方...如果重要,请使用 CSPRNG 播种您的 PRNG - 每个系统都有可以做的事情。
【解决方案2】:

随机数生成器从初始种子生成随机值的序列,并不意味着用于根据种子生成单个随机值。所以它应该用g.seed(seed)初始化,然后对所有(x, y)值按固定顺序调用,而不是每次都重新播种。这将有效地提供具有预期分布的随机值。

例如:

std::random_device rd;
std::mt19937 g{ rd() };
std::uniform_real_distribution<float> gen{ -1.0, 1.0 };

constexpr std::size_t nx = 100;
constexpr std::size_t nz = 100;
float noise[nx][nz];

void generateNoise() {
     g.seed(seed);
     for(int x = 0; x < nx; ++x) for(int x = 0; x < nx; ++x)
          noise[x][z] = gen(g);
     return gen(g);
}

【讨论】:

  • 我无法运行您的 sn-p。您会建议“种子”变量的值和使用“gen”函数的方法吗?谢谢。
【解决方案3】:

我不明白您为什么要不断地重新播种 - 这似乎毫无意义且缓慢。但这不是你要问的,所以...

  1. rand 产生质量很差的随机数。非常低的周期,通常基于线性同余生成器(不好)。此外,种子大小非常小。不要使用它 - &lt;random&gt; 的存在是有原因的。

  2. 您使用srand 播种的方式似乎很大程度上取决于您传入的xz 值,然后您乘以可能导致溢出和截断的大数srand,这意味着(由于可能的种子值数量有限)您将经常重复使用相同的种子。

您可能想访问的一些相关链接:

http://en.cppreference.com/w/cpp/numeric/random

https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

【讨论】:

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