我可以在现代 C++ 中排除随机数范围内的数字或数字子范围吗?
Can I exclude a number or subrange of numbers inside a range of random numbers in modern C++?
我有:
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> probability(0, 100);
我想排除这个概率范围内的一些数字。
示例 1:比方说,我想生成一个 0 到 100 之间的随机数,但这个数字永远不会是 4。
示例 2:比方说,我想生成一个 0 到 100 之间的随机数,但这个数字永远不能是 4 到 7 之间的任何数字。
我想知道是否可以在不使用 std::rand
的情况下在现代 C++ 中实现?
有一个选项可以在合理的数字范围内手动完成...,创建一个查找 table 并排除无效的数字:
static int rand_pool[]{1,2,3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23}; //no number 4
srand((int)time(0));
int random_number = rand_pool[rand() % 22];
如果你想错过 4 说,那么一个很好的方法(不损害生成器的任何统计属性)是在半开区间 [0, 99) 中绘制然后添加 1如果数字是 4 或更大。
您执行类似于省略范围内数字的操作。
此方法是对与所需概率分布关联的 分位数 函数建模的一种非常好的方法。
如果您想继续使用 uniform_int_distribution
,您可以像这样手动完成:
Example1: Let's say, I want to generate a random number in between 0 and 100, but this number can never be 4.
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> distribution(0,99);
auto temp = distribution(mt);
auto random_number = (temp < 4) ? temp : temp + 1;
Example2: Let's say, I want to generate a random number in between 0 and 100, but this number can never be any number between 4 and 7.
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> distribution(0,96);
auto temp = distribution(mt);
auto random_number = (temp < 4) ? temp : temp + 4;
这可以概括为编写函数 random_int_between_excluding(int first, int last, std::vector<int> exclude)
,但在某些时候遵循 NathanOlivers 的建议并使用 std::discrete_distribution
会更简单。
Example2: Let's say, I want to generate a random number in between 0
and 100, but this number can never be any number between 4 and 7.
这就是 std::piecewise_constant_distribution 的用途。
std::vector<int> i{0, 4, 8, 101};
std::vector<int> w{ 4, 0, 93};
std::piecewise_constant_distribution<> d(i.begin(), i.end(), w.begin());
您可以在均匀分布上使用任意复杂度的过滤器:
template<typename D, typename G, typename F>
auto sample(D &distribution, G &generator, F const &filter)
{
while(true)
{
auto const value = distribution(generator);
if(filter(value))
return value;
}
}
您的示例案例转换为以下内容
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> probability(0, 100);
auto const filter = +[](int n) {return n < 4 || n > 7;}
int const i = sample(probability, mt, filter);
你必须记住,这种过滤是有代价的。
令 N
为分布中不同值的数量 returns,F
- 这些值被过滤掉的数量;然后,如果您需要采样 S
个值,则必须平均采样和过滤 S * N / (N - F)
个值。如果 F 与 N 相比较小,那没关系,但是当 F 接近 N 时效率极低。在您的情况下,N = 100
、F = 4
和 N / (N - F) = 1.04166...
如果您更喜欢可读性和简单性,那是您的选择。否则,如果你需要性能,你最好尝试分段分布或手动弄乱值范围。
我有:
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> probability(0, 100);
我想排除这个概率范围内的一些数字。
示例 1:比方说,我想生成一个 0 到 100 之间的随机数,但这个数字永远不会是 4。
示例 2:比方说,我想生成一个 0 到 100 之间的随机数,但这个数字永远不能是 4 到 7 之间的任何数字。
我想知道是否可以在不使用 std::rand
的情况下在现代 C++ 中实现?
有一个选项可以在合理的数字范围内手动完成...,创建一个查找 table 并排除无效的数字:
static int rand_pool[]{1,2,3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23}; //no number 4
srand((int)time(0));
int random_number = rand_pool[rand() % 22];
如果你想错过 4 说,那么一个很好的方法(不损害生成器的任何统计属性)是在半开区间 [0, 99) 中绘制然后添加 1如果数字是 4 或更大。
您执行类似于省略范围内数字的操作。
此方法是对与所需概率分布关联的 分位数 函数建模的一种非常好的方法。
如果您想继续使用 uniform_int_distribution
,您可以像这样手动完成:
Example1: Let's say, I want to generate a random number in between 0 and 100, but this number can never be 4.
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> distribution(0,99);
auto temp = distribution(mt);
auto random_number = (temp < 4) ? temp : temp + 1;
Example2: Let's say, I want to generate a random number in between 0 and 100, but this number can never be any number between 4 and 7.
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> distribution(0,96);
auto temp = distribution(mt);
auto random_number = (temp < 4) ? temp : temp + 4;
这可以概括为编写函数 random_int_between_excluding(int first, int last, std::vector<int> exclude)
,但在某些时候遵循 NathanOlivers 的建议并使用 std::discrete_distribution
会更简单。
Example2: Let's say, I want to generate a random number in between 0 and 100, but this number can never be any number between 4 and 7.
这就是 std::piecewise_constant_distribution 的用途。
std::vector<int> i{0, 4, 8, 101};
std::vector<int> w{ 4, 0, 93};
std::piecewise_constant_distribution<> d(i.begin(), i.end(), w.begin());
您可以在均匀分布上使用任意复杂度的过滤器:
template<typename D, typename G, typename F>
auto sample(D &distribution, G &generator, F const &filter)
{
while(true)
{
auto const value = distribution(generator);
if(filter(value))
return value;
}
}
您的示例案例转换为以下内容
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> probability(0, 100);
auto const filter = +[](int n) {return n < 4 || n > 7;}
int const i = sample(probability, mt, filter);
你必须记住,这种过滤是有代价的。
令 N
为分布中不同值的数量 returns,F
- 这些值被过滤掉的数量;然后,如果您需要采样 S
个值,则必须平均采样和过滤 S * N / (N - F)
个值。如果 F 与 N 相比较小,那没关系,但是当 F 接近 N 时效率极低。在您的情况下,N = 100
、F = 4
和 N / (N - F) = 1.04166...
如果您更喜欢可读性和简单性,那是您的选择。否则,如果你需要性能,你最好尝试分段分布或手动弄乱值范围。