![](/img/trans.png)
[英]How to generate huge uniform distribution over small range of int in C++ fastly?
[英]Representing a uniform distribution over the range of an arbitrary enum type
我在很多地方使用C ++隨機數實用程序庫。 它可能不是很舒服(例如,沒有任意分布的基類),但是 - 我已經學會了忍受它。
現在我碰巧需要從枚舉類型中統一采樣值。 我知道,關於SO的問題已經存在:
然而,那一個:
假設所有枚舉值都是連續的,即它不起作用
enum Color { Red = 1, Green = 2, Blue = 4 }
我們希望以1/3的概率對這三個值中的每一個進行采樣。
std::uniform_distribution<>
的功能,即它不能與您傳遞它的隨機引擎一起使用等等。 顯然我不能使用std::uniform_int_distribution<Color>
,如果只是因為上面的原因1。 我該怎么做呢?
筆記:
使用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.