std::ranges::shuffle(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– std::ranges::shuffle(3) πŸ’–

okay bestie, this one is from the C++ Standard Libary manual (yes, that spelling is right there on the page, iykyk) and it’s literally the β€œmake my range go chaotic good” algorithm. ✨


🌸 NAME 🌸

std::ranges::shuffle β€” std::ranges::shuffle. That’s the name, and it’s serving exactly that. πŸ’…


πŸ¦‹ Synopsis πŸ¦‹

Defined in header <algorithm>.

πŸ’« Call signature πŸ’«

Overload (1) is since C++20, and it’s giving very β€œiterator pair + generator” energy:

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 shuffle( I first, S last, Gen&& gen );

Overload (2) is also since C++20, and it’s the range-y twin:

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> shuffle( R&& r, Gen&& gen );

So like, overload (1) reorders the elements in the given range [first, last) so that each possible permutation of those elements has equal probability of appearance. Equal probability, no cap. 🎲

Overload (2) is the same as (1), but it uses r as the range, as if using ranges::begin(r) as first and ranges::end(r) as last. Bestie behavior. 🫢

The function-like entities described on this page are niebloids, that is:

  • Explicit template argument lists cannot be specified when calling any of them.
  • None of them are visible to argument-dependent lookup.
  • When any of them are found by normal unqualified lookup as the name to the left of the function-call operator, argument-dependent lookup is inhibited.

In practice, they may be implemented as function objects, or with special compiler extensions.


🌼 Parameters 🌼

  • first, last β€” the range of elements to shuffle randomly
  • r β€” the range of elements to shuffle randomly
  • gen β€” the random number generator

✨ Return value ✨

An iterator equal to last. That’s it. That’s the return value. πŸ’–


πŸ”₯ Complexity πŸ”₯

Exactly (last - first) - 1 swaps. Not more, not less, periodt. πŸ’€


🧚 Possible implementation 🧚

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

πŸŽ€ Example πŸŽ€

// Run this code

#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

πŸ‘‘ See also πŸ‘‘

  • ranges::next_permutation β€” generates the next greater lexicographic permutation of a range of elements (C++20) (niebloid)
  • ranges::prev_permutation β€” generates the next smaller lexicographic permutation of a range of elements (C++20) (niebloid)
  • random_shuffle β€” randomly re-orders elements in a range (until C++17) (function template)
  • shuffle β€” randomly re-orders elements in a range (C++11) (function template)

okay that’s the whole page, bestie 🫢. Cute sign-off time: source is http://cppreference.com, version/date is 2024.06.10, and the page is std::ranges::shuffle(3). Mwah πŸ’‹βœ¨

πŸ“¦ where this page comes from