表示在任意枚举类型范围内的均匀分布

时间:2016-08-15 13:52:55

标签: c++ c++11 random enums uniform-distribution

我在很多地方使用C ++随机数实用程序库。它可能不太舒服(例如,没有任意分布的基类),但是 - 我已经学会了忍受它。

现在我碰巧需要从枚举类型中统一采样值。我知道,已经有关于SO的问题了:

generating random enums

然而,那个:

  1. 假设所有枚举值都是连续的,即它不适用于

    enum Color { Red = 1, Green = 2, Blue = 4 }
    

    我们希望以1/3的概率对这三个值中的每一个进行采样。

  2. 不提供std::uniform_distribution<>的功能,即它不适用于您传递它的随机引擎等等。
  3. 显然我不能使用std::uniform_int_distribution<Color>,如果只是出于上述原因1。我该怎么做呢?

    注意:

    • 代码必须是通用的,即枚举类型将是模板参数。
    • 因为我可能需要一些仪器而不仅仅是粗糙的枚举,你可能会认为我拥有它;只是明确陈述你的假设。
    • 具体而言,如果它有所帮助,假设我使用Better Enums,让我完全装满了所有的花里胡哨。
    • 如果某种惯用方式不做任何这样的仪器,那将会得到一个很好的答案,但我对此表示怀疑。
    • 只接受C ++ 11/14解决方案。
    • 具有相同值的多个枚举标识符不会使频率加倍,它们只是彼此的别名。如果您有一个简单的解决方案,假设这些不存在,那么这也是相关的,尽管不是最理想的。

3 个答案:

答案 0 :(得分:4)

使用Better Enums,可以通过以下方式解决此问题:

template<typename T>
typename T get_uniform_value(std::default_random_engine& eng)
{
    std::uniform_int_distribution<int> dist(0, T::_size() - 1);
    return T::_values()[dist(eng)];
}

用法示例:

BETTER_ENUM(Channel, int, Red, Green = 2, Blue) // Enum to generate random values of
...
std::default_random_engine rng(std::random_device{}());
Channel r = get_uniform_value<Channel>(rng); // Uniformly distributed between 0, 2 and 3

答案 1 :(得分:2)

这是分布的三种实现,按递增复杂度的顺序排列:

首先,如果我们可以依赖于值不同或者重复值超重,我们就可以将_values()容器编入索引:

template<class Enum>
struct SimpleEnumDistribution
{
    std::uniform_int_distribution<typename Enum::_integral> dist{0, Enum::_size() - 1};
    template<class Generator> Enum operator()(Generator& g) { return Enum::_values()[dist(g)]; }
};

否则,我们可以使用拒绝采样,预先计算枚举值范围的最小值和最大值:

template<class Enum>
struct UniformEnumDistribution
{
    std::uniform_int_distribution<typename Enum::_integral> dist{
        *std::min_element(Enum::_values().begin(), Enum::_values().end()),
        *std::max_element(Enum::_values().begin(), Enum::_values().end())};
    template<class Generator> Enum operator()(Generator& g)
    {
        for (;;)
            if (auto value = Enum::_from_integral_nothrow(dist(g)))
                return *value;
    }
};

如果这样效率低(可能枚举值很稀疏),我们可以在初始化时计算查找表:

template<class Enum>
struct FastUniformEnumDistribution
{
    std::uniform_int_distribution<std::size_t> dist;
    std::array<typename Enum::_integral, Enum::_size()> values;
    FastUniformEnumDistribution()
    {
        std::copy(Enum::_values().begin(), Enum::_values().end(), values.data());
        std::sort(values.begin(), values.end());
        dist.param(std::uniform_int_distribution<std::size_t>::param_type{0u, static_cast<std::size_t>(
            std::distance(values.begin(), std::unique(values.begin(), values.end())) - 1)});
    }
    template<class Generator> Enum operator()(Generator& g)
    {
        return Enum::_from_integral_unchecked(values[dist(g)]);
    }
};

Example

答案 2 :(得分:0)

the question you linked to中,假设您希望在枚举器值上进行统一分布。

然而,&#34;在枚举类型上均匀分布&#34;也可能意味着,在枚举的范围上均匀分布,这通常意味着实现选择的基础类型的所有可能值。

还有其他基本问题:

在您展示的情况下

enum Color { Red = 1, Green = 2, Blue = 4 }

据推测,你想要的均匀分布是从0到7(每个枚举器可以使用位掩码进行OR&#39; d。)

假设枚举是:

enum Color { Red = 1, Green = 2, Blue = 3 }

然后大概你只需要分发1,2,3。

我认为您无法期望编译器或任何模板代码能够理解您的意图 - 任何&#34; enum - &gt;均匀分布&#34;代码需要提示,以便它知道哪些枚举器应该与其他枚举器组合,哪些只是选项。

简而言之,我认为你应该完全按照你所关联的问题做什么,并在int或其他任何内容上生成适当的分布,然后static_cast将其添加到枚举。并且不要尝试使用一些模板解决方案,试图为每个可能的枚举阅读你的想法。