在 C++ 中,如何生成介于两个范围之间的随机数?
In C++, how can I generate a random number that falls between two ranges?
如何编写代码来生成介于两个不同范围之间的随机数?
示例:生成一个介于 5 到 7 或 10 到 12 之间的随机数。可能的结果是 5、6、7、10、11 或 12。
更新 下面添加了第二个实现
如果你想让它通用,你必须对其进行编码。
想到两个选项:
discrete_distribution(随便喂它5,6,7,10,11,12)
- 生成数字 [0..6) 并索引到数组中
int arr[]={5,6,7,10,11,12}
第二个:
#include <random>
#include <iostream>
int main()
{
using namespace std;
vector<int> arr = {5,6,7,10,11,12};
mt19937 prng { random_device {} () };
uniform_int_distribution<> dist(0, arr.size()-1);
int i = 10;
while (i--)
std::cout << arr[dist(prng)] << " ";
}
版画
5 5 6 12 11 6 11 12 5 12
或者,当然类似
更新
通过使用 Boost Interval Container Library 有效地表示构成域的间隔,将针对许多段或大段进行扩展的替代实现:
template <typename T = int>
struct uniform_draw {
using set = boost::icl::interval_set<T>;
using ival = typename set::interval_type::type;
uniform_draw(std::initializer_list<ival> data)
: set_(make_set(data)), dist_(0, set_.size() - 1)
{ }
friend std::ostream& operator<<(std::ostream& os, uniform_draw const& ud) {
return os << ud.set_ << " (#samples:" << ud.set_.size() << ")";
}
template <typename Engine>
T operator()(Engine& engine) {
uintmax_t index = dist_(engine);
std::cout << " - index: " << index << " against " << set_ << "\n";
// I think this can be optimized. I just don't know how to elegantly do that / yet
for (auto& iv : set_) {
std::cout << " - index: " << index << " against " << iv << "\n";
if (index > size(iv)) {
index -= size(iv);
} else {
return iv.lower() + index;
}
}
throw std::range_error("uniform_draw");
}
private:
set make_set(std::initializer_list<ival> data) {
set r;
for (auto& el : data)
r.insert(el);
return r;
}
set const set_;
std::uniform_int_distribution<T> dist_; // TODO make_unsigned<T>?
};
像平常的发行版一样使用它:
mt19937 mt { random_device {} () };
uniform_draw<int> dist { {5, 7}, {10, 12} };
std::cout << dist << "\n";
for (int i = 0; i < 10; ++i)
std::cout << "RESULT: " << dist(mt) << "\n";
打印例如:
{[5,7)[10,12)} (#samples:4)
7 7 6 11 6 6 7 7 7 6
选择一个随机数 0
或 1
。如果这个数字是0
,在第一个范围内选择一个随机数,否则在第二个范围内选择一个随机数。
如果您希望两个范围内的每个数字具有相等的概率,您可以根据两个范围的大小对第一个决定进行加权。
我看到有两种方法可以做到这一点。假设您的两个范围是 [minA, maxA] 和 [minB, maxB],其中 maxA < minB(如果不只是交换两个范围)
解决方案 1:
1) Generate a random number X in [minA, maxB] first
2) if X falls (maxA,minB) goto 1)
3) At this point X is the output
解决方案 2(更有效,尤其是当两个范围之间的差距很大时):
1) rangeA = maxA - minA, rangeB = maxB - minB;
2) generate a random number X within [0,rangeA+rangeB]
3) if X < rangeA then output minA+X else minB+X
如何编写代码来生成介于两个不同范围之间的随机数?
示例:生成一个介于 5 到 7 或 10 到 12 之间的随机数。可能的结果是 5、6、7、10、11 或 12。
更新 下面添加了第二个实现
如果你想让它通用,你必须对其进行编码。
想到两个选项:
discrete_distribution(随便喂它5,6,7,10,11,12)- 生成数字 [0..6) 并索引到数组中
int arr[]={5,6,7,10,11,12}
第二个:
#include <random>
#include <iostream>
int main()
{
using namespace std;
vector<int> arr = {5,6,7,10,11,12};
mt19937 prng { random_device {} () };
uniform_int_distribution<> dist(0, arr.size()-1);
int i = 10;
while (i--)
std::cout << arr[dist(prng)] << " ";
}
版画
5 5 6 12 11 6 11 12 5 12
或者,当然类似
更新
通过使用 Boost Interval Container Library 有效地表示构成域的间隔,将针对许多段或大段进行扩展的替代实现:
template <typename T = int>
struct uniform_draw {
using set = boost::icl::interval_set<T>;
using ival = typename set::interval_type::type;
uniform_draw(std::initializer_list<ival> data)
: set_(make_set(data)), dist_(0, set_.size() - 1)
{ }
friend std::ostream& operator<<(std::ostream& os, uniform_draw const& ud) {
return os << ud.set_ << " (#samples:" << ud.set_.size() << ")";
}
template <typename Engine>
T operator()(Engine& engine) {
uintmax_t index = dist_(engine);
std::cout << " - index: " << index << " against " << set_ << "\n";
// I think this can be optimized. I just don't know how to elegantly do that / yet
for (auto& iv : set_) {
std::cout << " - index: " << index << " against " << iv << "\n";
if (index > size(iv)) {
index -= size(iv);
} else {
return iv.lower() + index;
}
}
throw std::range_error("uniform_draw");
}
private:
set make_set(std::initializer_list<ival> data) {
set r;
for (auto& el : data)
r.insert(el);
return r;
}
set const set_;
std::uniform_int_distribution<T> dist_; // TODO make_unsigned<T>?
};
像平常的发行版一样使用它:
mt19937 mt { random_device {} () };
uniform_draw<int> dist { {5, 7}, {10, 12} };
std::cout << dist << "\n";
for (int i = 0; i < 10; ++i)
std::cout << "RESULT: " << dist(mt) << "\n";
打印例如:
{[5,7)[10,12)} (#samples:4)
7 7 6 11 6 6 7 7 7 6
选择一个随机数 0
或 1
。如果这个数字是0
,在第一个范围内选择一个随机数,否则在第二个范围内选择一个随机数。
如果您希望两个范围内的每个数字具有相等的概率,您可以根据两个范围的大小对第一个决定进行加权。
我看到有两种方法可以做到这一点。假设您的两个范围是 [minA, maxA] 和 [minB, maxB],其中 maxA < minB(如果不只是交换两个范围)
解决方案 1:
1) Generate a random number X in [minA, maxB] first
2) if X falls (maxA,minB) goto 1)
3) At this point X is the output
解决方案 2(更有效,尤其是当两个范围之间的差距很大时):
1) rangeA = maxA - minA, rangeB = maxB - minB;
2) generate a random number X within [0,rangeA+rangeB]
3) if X < rangeA then output minA+X else minB+X