std::ranges...tion_result(3)
π std::ranges...tion_result(3) π
Okay bestie, this is the std::ranges...tion_result(3) page from the C++ Standard Libary manual (yes, βLibaryβ with one r β we respect the lore). Itβs the group chat for std::ranges::next_permutation and std::ranges::next_permutation_result, and itβs giving lexicographic permutation main-character energy. β¨
π NAME π
std::ranges::next_permutation,std::ranges::next_permutation_resultβstd::ranges::next_permutation,std::ranges::next_permutation_result
πΈ Synopsis πΈ
Defined in header <algorithm>.
Call signature
(1) (since C++20)
template< std::bidirectional_iterator I, std::sentinel_for<I> S,
class Comp = ranges::less, class Proj = std::identity >
requires std::sortable<I, Comp, Proj>
constexpr next_permutation_result<I>
next_permutation( I first, S last, Comp comp = {}, Proj proj = {} );
(2) (since C++20)
template< ranges::bidirectional_range R, class Comp = ranges::less,
class Proj = std::identity >
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
constexpr next_permutation_result<ranges::borrowed_iterator_t<R>>
next_permutation( R&& r, Comp comp = {}, Proj proj = {} );
Helper type
(3) (since C++20)
template< class I >
using next_permutation_result = ranges::in_found_result<I>;
1) Transforms the range [first, last) into the next permutation, where the set of all permutations is ordered lexicographically with respect to binary comparison function object comp and projection function object proj. Returns {last, true} if such a βnext permutationβ exists; otherwise transforms the range into the lexicographically first permutation as if by ranges::sort(first, last, comp, proj), and returns {last, false}.
2) Same as (1), but uses r as the source 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, 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. iykyk π
π«Ά Parameters π«Ά
first,lastβ the range of elements to permute.rβ the range of elements to permute.compβ comparison function object which returnstrueif the first argument is less than the second.projβ projection to apply to the elements.
π Return value π
- 1)
ranges::next_permutation_result<I>{last, true}if the new permutation is lexicographically greater than the old one.ranges::next_permutation_result<I>{last, false}if the last permutation was reached and the range was reset to the first permutation. - 2) Same as (1) except that the return type is
ranges::next_permutation_result<ranges::borrowed_iterator_t<R>>.
π Exceptions π
Any exceptions thrown from iterator operations or the element swap. Thatβs the whole tea. π΅
π₯ Complexity π₯
At most \(\scriptsize N/2\)N / 2 swaps, where \(\scriptsize N\)N is ranges::distance(first, last) in case (1) or ranges::distance(r) in case (2). Averaged over the entire sequence of permutations, typical implementations use about 3 comparisons and 1.5 swaps per call.
π§ Notes π§
Implementations (e.g. MSVC STL) may enable vectorization when the iterator type models contiguous_iterator and swapping its value type calls neither non-trivial special member function nor ADL-found swap.
π Possible implementation π
This is the implementation vibe from the page β line breaks are canon, do not come for them:
struct next_permutation_fn {
template<std::bidirectional_iterator I, std::sentinel_for<I> S,
class Comp = ranges::less, class Proj = std::identity>
requires std::sortable<I, Comp, Proj>
constexpr ranges::next_permutation_result<I>
operator()(I first, S last, Comp comp = {}, Proj proj = {})
const
{
// check that the sequence has at least two elements
if (first == last)
return {std::move(first), false};
I i_last{ranges::next(first, last)};
I i{i_last};
if (first == --i)
return {std::move(i_last), false};
// main "permutating" loop
for (;;)
{
I i1{i};
if (std::invoke(comp, std::invoke(proj, *--i),
std::invoke(proj, *i1)))
{
I j{i_last};
while (!std::invoke(comp, std::invoke(proj, *i),
std::invoke(proj, *--j)))
{}
std::iter_swap(i, j);
std::reverse(i1, i_last);
return {std::move(i_last), true};
}
// permutation "space" is exhausted
if (i == first)
{
std::reverse(first, i_last);
return {std::move(i_last), false};
}
}
}
template<ranges::bidirectional_range R, class Comp = ranges::less,
class Proj = std::identity>
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
constexpr
ranges::next_permutation_result<ranges::borrowed_iterator_t<R>>
operator()(R&& r, Comp comp = {}, Proj proj = {}) const
{
return (*this)(ranges::begin(r), ranges::end(r),
std::move(comp), std::move(proj));
} };
inline constexpr next_permutation_fn next_permutation {};
π Example π
Run this code, bestie, it eats π
#include <algorithm>
#include <array>
#include <compare>
#include <functional>
#include <iostream>
#include <string>
struct S
{
char c;
int i;
auto operator<=>(const S&) const = default;
friend std::ostream& operator<<(std::ostream& os, const S& s)
{
return os << "{'" << s.c << "', " << s.i << "}";
}
};
auto print = [](auto const& v, char term = ' ')
{
std::cout << "{ ";
for (const auto& e : v)
std::cout << e << ' ';
std::cout << '}' << term;
};
int main()
{
std::cout << "Generate all permutations (iterators case):\n";
std::string s{"abc"};
do
{
print(s);
}
while (std::ranges::next_permutation(s.begin(), s.end()).found);
std::cout << "\n" "Generate all permutations (range case):\n";
std::array a{'a', 'b', 'c'};
do
{
print(a);
}
while (std::ranges::next_permutation(a).found);
std::cout << "\n" "Generate all permutations using comparator:\n";
using namespace std::literals;
std::array z{"β"s, "β"s, "β"s};
do
{
print(z);
}
while (std::ranges::next_permutation(z, std::greater()).found);
std::cout << "\n" "Generate all permutations using projection:\n";
std::array<S, 3> r{S{'A',3}, S{'B',2}, S{'C',1}};
do
{
print(r, '\n');
}
while (std::ranges::next_permutation(r, {}, &S::c).found);
}
π Output π
Generate all permutations (iterators case):
{ a b c } { a c b } { b a c } { b c a } { c a b } { c b a }
Generate all permutations (range case):
{ a b c } { a c b } { b a c } { b c a } { c a b } { c b a }
Generate all permutations using comparator:
{ β β β } { β β β } { β β β } { β β β } { β β β } { β β β }
Generate all permutations using projection:
{ {'A', 3} {'B', 2} {'C', 1} }
{ {'A', 3} {'C', 1} {'B', 2} }
{ {'B', 2} {'A', 3} {'C', 1} }
{ {'B', 2} {'C', 1} {'A', 3} }
{ {'C', 1} {'A', 3} {'B', 2} }
{ {'C', 1} {'B', 2} {'A', 3} }
π¦ See also π¦
ranges::prev_permutation(C++20) (niebloid) β generates the next smaller lexicographic permutation of a range of elements.ranges::is_permutation(C++20) (niebloid) β determines if a sequence is a permutation of another sequence.next_permutation(function template) β generates the next greater lexicographic permutation of a range of elements.prev_permutation(function template) β generates the next smaller lexicographic permutation of a range of elements.is_permutation(C++11) (function template) β determines if a sequence is a permutation of another sequence.
Source: http://cppreference.com Β· date: 2024.06.10 Β· page: std::ranges...tion_result(3) β mwah, ily for reading the whole thing, bestie πβ¨π«Ά
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms