Skip to main content

std::ranges::shuffle() algorithm

// (1)
I shuffle( I first, S last, Gen&& gen );

// (2)
ranges::borrowed_iterator_t<R> shuffle( R&& r, Gen&& gen );

The type of arguments are generic and have the following constraints:

  • I - std::random_access_iterator
  • S - std::sentinel_for<I>
  • R - std::ranges::random_access_range
  • G - (none)

Additionally, each overload has the following constraints:

  • (1) - permutable<I> && uniform_random_bit_generator<remove_reference_t<Gen>>
  • (2) - permutable<ranges::iterator_t<R>> && uniform_random_bit_generator<remove_reference_t<Gen>>

(The std:: namespace was omitted for readability)

  • (1) Reorders the elements in the given range [first; last) such that each possible permutation of those elements has equal probability of appearance.

  • (2) Same as (1), but uses r as the range, as if using ranges::begin(r) as first and ranges::end(r) as last.

The function-like entities described on this page are niebloids.

Parameters

first
last

The range of elements to shuffle.

r

The range of elements to shuffle.

gen

The random number generator.

Return value

An iterator equal to last.

Complexity

Exactly (last - first) - 1 swaps.

Exceptions

(none)

Possible implementation

shuffle(1) and shuffle(2)
struct shuffle_fn
{
template<std::random_access_iterator I, std::sentinel_for<I> S, class Gen>
requires std::permutable<I> &&
std::uniform_random_bit_generator<std::remove_reference_t<Gen>>
I operator()(I first, S last, Gen&& gen) const
{
using diff_t = std::iter_difference_t<I>;
using distr_t = std::uniform_int_distribution<diff_t>;
using param_t = typename distr_t::param_type;
distr_t D;
const auto n {last - first};
for (diff_t i {1}; i < n; ++i)
ranges::iter_swap(first + i, first + D(gen, param_t(0, i)));
return ranges::next(first, last);
}

template<ranges::random_access_range R, class Gen>
requires std::permutable<ranges::iterator_t<R>> &&
std::uniform_random_bit_generator<std::remove_reference_t<Gen>>
ranges::borrowed_iterator_t<R> operator()(R&& r, Gen&& gen) const
{
return (*this)(ranges::begin(r), ranges::end(r), std::forward<Gen>(gen));
}
};

inline constexpr shuffle_fn shuffle {};

Examples

Main.cpp
#include <algorithm>
#include <array>
#include <iostream>
#include <random>

void print(const auto& a)
{
for (const auto e : a)
std::cout << e << ' ';
std::cout << '\n';
}

int main()
{
std::array a {'A', 'B', 'C', 'D', 'E', 'F'};
print(a);

std::random_device rd;
std::mt19937 gen {rd()};

for (int i {}; i != 3; ++i)
{
std::ranges::shuffle(a, gen);
print(a);
}
}
Possible Output
A B C D E F
F E A C D B
E C B F A D
B A E C F D
This article originates from this CppReference page. It was likely altered for improvements or editors' preference. Click "Edit this page" to see all changes made to this document.
Hover to see the original license.

std::ranges::shuffle() algorithm

// (1)
I shuffle( I first, S last, Gen&& gen );

// (2)
ranges::borrowed_iterator_t<R> shuffle( R&& r, Gen&& gen );

The type of arguments are generic and have the following constraints:

  • I - std::random_access_iterator
  • S - std::sentinel_for<I>
  • R - std::ranges::random_access_range
  • G - (none)

Additionally, each overload has the following constraints:

  • (1) - permutable<I> && uniform_random_bit_generator<remove_reference_t<Gen>>
  • (2) - permutable<ranges::iterator_t<R>> && uniform_random_bit_generator<remove_reference_t<Gen>>

(The std:: namespace was omitted for readability)

  • (1) Reorders the elements in the given range [first; last) such that each possible permutation of those elements has equal probability of appearance.

  • (2) Same as (1), but uses r as the range, as if using ranges::begin(r) as first and ranges::end(r) as last.

The function-like entities described on this page are niebloids.

Parameters

first
last

The range of elements to shuffle.

r

The range of elements to shuffle.

gen

The random number generator.

Return value

An iterator equal to last.

Complexity

Exactly (last - first) - 1 swaps.

Exceptions

(none)

Possible implementation

shuffle(1) and shuffle(2)
struct shuffle_fn
{
template<std::random_access_iterator I, std::sentinel_for<I> S, class Gen>
requires std::permutable<I> &&
std::uniform_random_bit_generator<std::remove_reference_t<Gen>>
I operator()(I first, S last, Gen&& gen) const
{
using diff_t = std::iter_difference_t<I>;
using distr_t = std::uniform_int_distribution<diff_t>;
using param_t = typename distr_t::param_type;
distr_t D;
const auto n {last - first};
for (diff_t i {1}; i < n; ++i)
ranges::iter_swap(first + i, first + D(gen, param_t(0, i)));
return ranges::next(first, last);
}

template<ranges::random_access_range R, class Gen>
requires std::permutable<ranges::iterator_t<R>> &&
std::uniform_random_bit_generator<std::remove_reference_t<Gen>>
ranges::borrowed_iterator_t<R> operator()(R&& r, Gen&& gen) const
{
return (*this)(ranges::begin(r), ranges::end(r), std::forward<Gen>(gen));
}
};

inline constexpr shuffle_fn shuffle {};

Examples

Main.cpp
#include <algorithm>
#include <array>
#include <iostream>
#include <random>

void print(const auto& a)
{
for (const auto e : a)
std::cout << e << ' ';
std::cout << '\n';
}

int main()
{
std::array a {'A', 'B', 'C', 'D', 'E', 'F'};
print(a);

std::random_device rd;
std::mt19937 gen {rd()};

for (int i {}; i != 3; ++i)
{
std::ranges::shuffle(a, gen);
print(a);
}
}
Possible Output
A B C D E F
F E A C D B
E C B F A D
B A E C F D
This article originates from this CppReference page. It was likely altered for improvements or editors' preference. Click "Edit this page" to see all changes made to this document.
Hover to see the original license.