如何在指定整数范围随机选多值?寻求替代std::sample冗余实现的方案
如何在整数范围内随机选取多个值?
你当前的实现通过创建完整的整数向量再调用std::sample,但当范围极大时,这种方式会占用大量不必要的内存,完全可以优化。标准C++库中并没有直接提供sample(int first, int last, ...)或ranges::sample(int n, ...)这类直接针对整数范围的抽样函数,但我们可以基于标准工具实现更高效的版本,避免冗余内存占用:
方案1:C++20及以上——用视图生成整数范围
C++20的范围库提供了std::views::iota,可以直接生成一个整数范围的视图(不存储实际元素,仅在遍历的时候生成值),配合std::sample使用非常简洁:
#include <ranges> #include <algorithm> #include <random> #include <vector> void Sample(int first, int last, std::vector<int>* out, std::size_t n, std::mt19937* g) { out->clear(); if (n == 0 || first >= last) return; auto int_range = std::views::iota(first, last); std::sample(int_range.begin(), int_range.end(), std::back_inserter(*out), n, *g); }
方案2:自定义计数迭代器(兼容C++11及以上)
如果你的项目还不能使用C++20,可以自定义一个简单的计数迭代器,模拟整数序列的遍历,同样不需要存储完整的整数向量:
#include <iterator> #include <algorithm> #include <random> #include <vector> // 模拟整数序列的输入迭代器 struct CountingIterator { using value_type = int; using difference_type = std::ptrdiff_t; using pointer = const int*; using reference = const int&; using iterator_category = std::input_iterator_tag; int current; explicit CountingIterator(int val) : current(val) {} value_type operator*() const { return current; } CountingIterator& operator++() { ++current; return *this; } CountingIterator operator++(int) { CountingIterator temp = *this; ++current; return temp; } bool operator==(const CountingIterator& other) const { return current == other.current; } bool operator!=(const CountingIterator& other) const { return !(*this == other); } }; void Sample(int first, int last, std::vector<int>* out, std::size_t n, std::mt19937* g) { out->clear(); if (n == 0 || first >= last) return; std::sample(CountingIterator(first), CountingIterator(last), std::back_inserter(*out), n, *g); }
方案3:小样本场景优化——直接生成不重复随机数
如果需要抽取的样本数量n远小于整数范围的大小,可以直接用随机数生成配合集合去重,避免遍历整个范围:
#include <random> #include <vector> #include <unordered_set> void Sample(int first, int last, std::vector<int>* out, std::size_t n, std::mt19937* g) { out->clear(); if (n == 0 || first >= last) return; const std::size_t range_size = static_cast<std::size_t>(last - first); if (n >= range_size) { // 样本数量大于等于范围大小,直接取全部元素 for (int i = first; i < last; ++i) { out->push_back(i); } return; } std::uniform_int_distribution<int> dist(first, last - 1); std::unordered_set<int> selected; while (selected.size() < n) { selected.insert(dist(*g)); } out->assign(selected.begin(), selected.end()); }
内容的提问来源于stack exchange,提问作者ALittleDiff
相关产品推荐
相关产品推荐

