改变 uniform_int_distribution 的范围

33

所以我有一个随机对象:

typedef unsigned int uint32;

class Random {
public:
    Random() = default;
    Random(std::mt19937::result_type seed) : eng(seed) {}

private:
    uint32 DrawNumber();
    std::mt19937 eng{std::random_device{}()};
    std::uniform_int_distribution<uint32> uniform_dist{0, UINT32_MAX};
};

uint32 Random::DrawNumber()
{
    return uniform_dist(eng);
}

我如何以最佳方式改变(通过另一个函数或其他方式)分布的上限?

(也愿意听取关于其他样式问题的建议)


在首次初始化后,您无法更改“分布”的边界。您应该如何继续取决于您使用随机数的目的以及为什么需要更改边界。 - us2012
@us2012 这是错误的;你可以。 - underscore_d
可能是使用std::uniform_int_distribution并稍后定义其范围的重复问题(预先防止反对意见:是的,那个更新,标题有点离题,但在我看来,它有一个更相关的被接受答案,并且通常更快地到达要点)。 - underscore_d
1
冒着有偏见的风险,这两个不同的问题基本上有相同的被接受答案 - “另一种方法是添加一个方法…” 另一个问题的答案部分与此处的被接受答案相同。此答案还包括使用std::mt19937的用法。话虽如此,.param方法是新的。由于这个问题现在已经五年了,也许最好的做法是将另一个标记为重复,并修改此处的被接受答案?或者只是保留问题不变 :) - LordAro
3个回答

56
分布对象轻便。当你需要一个随机数时,只需构造一个新的分布即可。我在游戏引擎中使用这种方法,在基准测试后,它与使用经典的rand()相当。
另外,我曾在GoingNative 2013直播中询问如何改变分布范围,标准委员会成员Stephen T. Lavavej建议简单地创建新的分布,因为这不应该是性能问题。
以下是你的代码的编写方式:
using uint32 = unsigned int;

class Random {
public:
    Random() = default;
    Random(std::mt19937::result_type seed) : eng(seed) {}
    uint32 DrawNumber(uint32 min, uint32 max);

private:        
    std::mt19937 eng{std::random_device{}()};
};

uint32 Random::DrawNumber(uint32 min, uint32 max)
{
    return std::uniform_int_distribution<uint32>{min, max}(eng);
}

1
我想我刚刚看了那个Going Native的流 :) 尽管我对C++(特别是C++11)还有点陌生,其中很多内容都超出了我的理解范围 :L如果你确定每次创建新分布没有显着的开销,我可能会选择这个。 - LordAro
4
我已进行了基准测试。开销很小,可以忽略不计。(这是由一个微优化控所说的)。此外,没有其他方法可以更改分布的边界。 - Vittorio Romeo
好的,非常感谢 :) 我可能会选择一个常量0作为最小值,并将UINT32_MAX作为默认最大值。 - LordAro
2
这将创建一个新的分布,而不是修改现有的分布。因此,无论类实例化的速度如何,它都不能回答问题。 - Brett Hale
通过谷歌找到这个答案,我有一些疑虑。你每次构造Random时都会创建一个新的std::mt19937引擎对象;这完全与S.T.L.建议的相反... - Casey

14

您可以简单地创建一个std::uniform_int_distribution<uint32>::param_type并使用param()方法修改范围。您可以使用decltype减少模板噪音:

decltype(uniform_dist.param()) new_range (0, upper);
uniform_dist.param(new_range);

7
我正在为我的示例将DrawNumber函数更改为public。您可以提供一个重载函数,该函数带有一个上限参数,并将一组新的uniform_int_distribution :: param_type传递给uniform_int_distribution::operator()param_type可以使用与相应分布相同的参数构造。
来自N3337,《§26.5.1.6/9 [rand.req.dist]

对于每个接受与分布参数对应的参数的D构造函数,P都必须有一个相应的构造函数,满足相同的要求并且接受数量,类型和默认值相同的参数。此外,对于返回与分布参数对应的值的D的每个成员函数,P都必须有一个相应的以相同名称,类型和语义的成员函数。

其中D是随机数分布函数对象的类型,P是由D的关联param_type命名的类型。
#include <iostream>
#include <random>

typedef unsigned int uint32;

class Random {
public:
    Random() = default;
    Random(std::mt19937::result_type seed) : eng(seed) {}

    uint32 DrawNumber();
    uint32 DrawNumber(uint32 ub);

private:
    std::mt19937 eng{std::random_device{}()};
    std::uniform_int_distribution<uint32> uniform_dist{0, UINT32_MAX};
};

uint32 Random::DrawNumber()
{
    return uniform_dist(eng);
}

uint32 Random::DrawNumber(uint32 ub)
{
    return uniform_dist(eng, decltype(uniform_dist)::param_type(0, ub));
}

int main()
{
  Random r;
  std::cout << r.DrawNumber() << std::endl;
  std::cout << r.DrawNumber(42) << std::endl;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接