std::sample() 具有整数范围

ALi*_*iff 4 c++ random algorithm c++20

如何在任何给定的整数范围内随机选取多个值?
对于std::sample()
一种可能的实现是:

void Sample(
    int first, int last, std::vector<int> *out, std::size_t n, std::mt19937 *g)
{
    std::vector<int> in{};
    for (int i = first; i < last; ++i)
    {
        in.emplace_back(i);
    }

    std::sample(in.begin(), in.end(), std::back_inserter(*out), n, *g);
}
Run Code Online (Sandbox Code Playgroud)

但我很犹豫,因为创建in向量似乎多余且不切实际,
特别是当first是 0 并且last是非常大的数字时。
是否有任何公共实现或库可以满足此要求?例如:

  • sample(int first, int last, ...)或者,
  • ranges::sample(int n, ...)

康桓瑋*_*康桓瑋 5

In C++20 you can sample on iota_view

#include <ranges>
#include <vector>
#include <algorithm>
#include <random>

void Sample(
  int first, int last, std::vector<int> *out, std::size_t n, std::mt19937 *g) 
{
  std::ranges::sample(
    std::views::iota(first, last), std::back_inserter(*out), n, *g);  
}
Run Code Online (Sandbox Code Playgroud)

Demo

Note that the above does not work in libstdc++ because ranges::sample incorrectly uses the std::sample's implementation. Since iota_view's iterator only satisfies Cpp17InputIterator, std::sample requires that the output range must be a random access iterator, which is not the case for back_inserter_iterator.

The workaround is to pre-resize the vector.

void Sample(
  int first, int last, std::vector<int> *out, std::size_t n, std::mt19937 *g) 
{
  out->resize(n);
  std::ranges::sample(
    std::views::iota(first, last), out->begin(), n, *g);  
}
Run Code Online (Sandbox Code Playgroud)

which works in both libstdc++ and MSVC-STL.