簡體   English   中英

表示在任意枚舉類型范圍內的均勻分布

[英]Representing a uniform distribution over the range of an arbitrary enum type

我在很多地方使用C ++隨機數實用程序庫。 它可能不是很舒服(例如,沒有任意分布的基類),但是 - 我已經學會了忍受它。

現在我碰巧需要從枚舉類型中統一采樣值。 我知道,關於SO的問題已經存在:

生成隨機枚舉

然而,那一個:

  1. 假設所有枚舉值都是連續的,即它不起作用

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

    我們希望以1/3的概率對這三個值中的每一個進行采樣。

  2. 不提供std::uniform_distribution<>的功能,即它不能與您傳遞它的隨機引擎一起使用等等。

顯然我不能使用std::uniform_int_distribution<Color> ,如果只是因為上面的原因1。 我該怎么做呢?

筆記:

  • 代碼必須是通用的,即枚舉類型將是模板參數。
  • 因為我可能需要一些儀器而不僅僅是粗糙的枚舉,你可能會認為我擁有它; 只是明確陳述你的假設。
  • 具體來說,如果它有所幫助,假設我使用了更好的Enums ,讓我充滿了所有的花里胡哨。
  • 如果某種慣用的方式不做任何這樣的儀器,那將會有一個很好的答案,但我對此表示懷疑。
  • 僅C ++ 11/14解決方案是可以接受的。
  • 具有相同值的多個枚舉標識符不會使頻率加倍,它們只是彼此的別名。 如果您有一個簡單的解決方案,假設這些不存在,那么這也是相關的,盡管不是最理想的。

使用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

以復雜度遞增的順序,這是分布的三種實現:

首先,如果我們可以依賴於值不同或者重復值超重,我們就可以索引_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)]);
    }
};

例子

我會說更慣用的是創建一個數組並從數組中選擇索引:

 template <typename Rnd>
 Color RandomColor(Rnd& rnd)
 {
     const std::array<Color, 3u> colors {Color::Red, Color::Green, Color::Blue};

     std::uniform_int_distribution<int> dist(0, colors.size() - 1);
     return colors[dist(rnd)];
 }

更好的枚舉似乎允許不使用Color::_values手動創建數組:

 template <typename BetterEnum, typename Rnd>
 BetterEnum RandomBetterEnum(Rnd& rnd)
 {
     std::uniform_int_distribution<int> dist(0, BetterEnum::_size() - 1);
     return BetterEnum::_values()[dist(rnd)];
 }

您鏈接的問題中 ,假設您希望統計分布在枚舉器值上

然而,“在枚舉類型上的均勻分布”也可能意味着在枚舉范圍內的均勻分布,這通常意味着由實現選擇的基礎類型的所有可能值。

還有其他基本問題:

在您展示的情況下

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

據推測,您想要的均勻分布是0到7(每個枚舉器可以使用位掩碼進行OR運算)。

假設枚舉是:

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

那么大概你只想在你的發行版中使用1,2,3。

我認為你不能指望編譯器或任何模板代碼理解你的意圖 - 任何“enum - > uniform distribution”代碼都需要提示,以便它知道哪些枚舉器應該與其他枚舉器的組合以及哪些只是選項。

簡而言之,我認為你應該完全按照你所鏈接的問題做什么,並在int或者其他任何東西上生成適當的分布,然后static_cast it到enum。 並且不要嘗試使用一些模板解決方案,試圖為每個可能的枚舉讀取你的想法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM