How can I write code to generate a random number that falls between two different ranges?
Example: Generate a random number that is between 5 and 7 or between 10 and 12. Possible outcomes are 5, 6, 7, 10, 11, or 12.
UPDATE Second implementation added below
If you want to make this generic, you'll have to code it.
Two options come to mind:
int arr[]={5,6,7,10,11,12}The second:
Live On Coliru
#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)] << " ";
}
Prints
5 5 6 12 11 6 11 12 5 12
Or, similar of course
An alternative implementation that will scale for many segments or large segments, by using Boost Interval Container Library to efficiently represent the intervals that make up the domain:
Live On Coliru
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>?
};
Use it like your usual distribution:
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";
Prints e.g.:
{[5,7)[10,12)} (#samples:4)
7 7 6 11 6 6 7 7 7 6
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With