Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::sample() with integer range

How can I pick multiple values randomly in any given integer range?
With std::sample(),
one possible implementation would be:

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);
}

But I'm hesitating because creating in vector seems redundant and impractical,
especially when first is 0 and last is very big number.
Is there any public implementation or library for this requirement? e.g.:

  • sample(int first, int last, ...) or,
  • ranges::sample(int n, ...)
like image 701
ALittleDiff Avatar asked Oct 24 '25 14:10

ALittleDiff


1 Answers

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);  
}

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);  
}

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

like image 101
康桓瑋 Avatar answered Oct 27 '25 09:10

康桓瑋