From b846678d29b62e116462763030c09e545b7ed616 Mon Sep 17 00:00:00 2001 From: szdytom Date: Wed, 19 Jul 2023 18:33:19 +0800 Subject: [PATCH] add uniform int sequence with unique elements --- libvmake/examples/random_numbers.cpp | 1 + libvmake/vmake.hpp | 62 ++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/libvmake/examples/random_numbers.cpp b/libvmake/examples/random_numbers.cpp index f3d6237..4528fb7 100644 --- a/libvmake/examples/random_numbers.cpp +++ b/libvmake/examples/random_numbers.cpp @@ -4,6 +4,7 @@ using namespace std; int main() { vmake::outputln(cout, " ", vmake::take(vmake::rng::uniform_ints(1, 10), 15)); + vmake::outputln(cout, " ", vmake::take(vmake::rng::uniform_ints(vmake::rng::require_unique, 1, 20), 15)); vmake::outputln(cout, " ", vmake::take(vmake::rng::uniform_reals(1., 10.), 5)); return 0; } diff --git a/libvmake/vmake.hpp b/libvmake/vmake.hpp index 7a48697..a3c80a9 100644 --- a/libvmake/vmake.hpp +++ b/libvmake/vmake.hpp @@ -517,6 +517,68 @@ inline auto uniform_ints(Tval &&l, Tval &&r) { }); } +struct require_unique_t {} require_unique; + +namespace details { + +template +struct unique_ints_sequence { + using result = Tval; + + unique_ints_sequence(unique_ints_sequence &&) = default; + unique_ints_sequence(const unique_ints_sequence &) = default; + + Tval l, r; + Engine rng; + std::uniform_int_distribution dis; + std::unordered_set used; + std::vector rest; + bool halfed; + + unique_ints_sequence(Engine &&e, Tval &&l, Tval &&r) : l(l), r(r), rng(std::forward(e)) + , dis(std::forward(l), std::forward(r)), used(), rest(), halfed(false) {} + + bool is_terminated() const noexcept { + return halfed && rest.empty(); + } + + auto operator()() { + if (!halfed && (used.size() + 1) * 2 >= static_cast<_unsigned_Tval>(r - l + 1)) { + for (Tval i = l; i <= r; ++i) { + if (!used.count(i)) rest.push_back(i); + } + std::shuffle(rest.begin(), rest.end(), rng); + halfed = true; + } + + if (halfed) { + if (rest.empty()) throw sequence_terminated_error(); + auto res = std::move(rest.back()); + rest.pop_back(); + return res; + } + + while (true) { + auto res = dis(rng); + if (used.count(res)) continue; + used.insert(res); + return res; + } + } + +private: + using _unsigned_Tval = typename std::conditional::value + , typename std::make_unsigned::type, Tval>::type; +}; + +}; + +template +inline auto uniform_ints(require_unique_t _, Tval &&l, Tval &&r) { + return details::unique_ints_sequence::type, Engine>(Engine(make_seed()) + , std::forward(l), std::forward(r)); +} + template inline auto uniform_reals(Tval &&l, Tval &&r) { return generate([rng = Engine(make_seed())