std::ranges..._permutation(3)
std::ranges..._permutation(3) β¨π
Okay so this is from the C++ Standard Libary manual (yes, βLibaryβ is literally how the header spells it, and we are choosing peace π), and the page title is std::ranges..._permutation(3). The actual main character here is std::ranges::is_permutation, and she is lowkey iconic.
π NAME π
std::ranges::is_permutation β std::ranges::is_permutation. Thatβs the whole name, bestie, weβre not doing extra.
πΈ Synopsis πΈ
Defined in header
<algorithm>
Call signature
Overload (1), since C++20:
template< std::forward_iterator I1, std::sentinel_for<I1> S1,
std::forward_iterator I2, std::sentinel_for<I2> S2,
class Proj1 = std::identity, class Proj2 = std::identity,
std::indirect_equivalence_relation<std::projected<I1, Proj1>,
std::projected<I2, Proj2>>
Pred = ranges::equal_to >
constexpr bool
is_permutation( I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {},
Proj1 proj1 = {}, Proj2 proj2 = {} );
Overload (2), since C++20:
template< ranges::forward_range R1, ranges::forward_range R2,
class Proj1 = std::identity, class Proj2 = std::identity,
std::indirect_equivalence_relation<
std::projected<ranges::iterator_t<R1>, Proj1>,
std::projected<ranges::iterator_t<R2>, Proj2>>
Pred = ranges::equal_to >
constexpr bool
is_permutation( R1&& r1, R2&& r2, Pred pred = {},
Proj1 proj1 = {}, Proj2 proj2 = {} );
So (1) returns true if there exists a permutation of the elements in range [first1, last1) that makes the range equal to [first2, last2) β after applying the corresponding projections Proj1, Proj2, and using the binary predicate Pred as a comparator. Otherwise it returns false. No cap.
And (2) is the same as (1), but it uses r1 as the first source range and r2 as the second source range, as if using ranges::begin(r1) as first1, ranges::end(r1) as last1, ranges::begin(r2) as first2, and ranges::end(r2) as last2.
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 π«Ά
first1,last1β the first range of the elementsfirst2,last2β the second range of the elementsr1β the first range of the elementsr2β the second range of the elementspredβ predicate to apply to the projected elementsproj1β projection to apply to the elements in the first rangeproj2β projection to apply to the elements in the second range
π Return value π
true if the range [first1, last1) is a permutation of the range [first2, last2). Thatβs the vibe check, and she either passes or she doesnβt π
π₯ Complexity π₯
At most O(N^2) applications of the predicate and each projection, or exactly N if the sequences are already equal, where N is ranges::distance(first1, last1). But if ranges::distance(first1, last1) != ranges::distance(first2, last2), no applications of the predicate and projections are made. Zero. None. The efficiency ate.
π¦ Notes π¦
The permutation relation is an equivalence relation. Periodt.
ranges::is_permutation can be used in testing, e.g. to check the correctness of rearranging algorithms such as sorting, shuffling, partitioning. If p is an original sequence and q is a βmutatedβ sequence, then ranges::is_permutation(p, q) == true means that q consists of βthe sameβ elements (maybe permuted) as p. Thatβs the whole point, bestie.
π§ Possible implementation π§
struct is_permutation_fn {
template<std::forward_iterator I1, std::sentinel_for<I1> S1,
std::forward_iterator I2, std::sentinel_for<I2> S2,
class Proj1 = std::identity, class Proj2 = std::identity,
std::indirect_equivalence_relation<std::projected<I1, Proj1>,
std::projected<I2, Proj2>>
Pred = ranges::equal_to>
constexpr bool operator()(I1 first1, S1 last1, I2 first2, S2 last2,
Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const
{
// skip common prefix
auto ret = std::ranges::mismatch(first1, last1, first2, last2,
std::ref(pred), std::ref(proj1), std::ref(proj2));
first1 = ret.in1, first2 = ret.in2;
// iterate over the rest, counting how many times each element
// from [first1, last1) appears in [first2, last2)
for (auto i {first1}; i != last1; ++i)
{
const auto i_proj {std::invoke(proj1, *i)};
auto i_cmp = [&]<typename T>(T&& t)
{
return std::invoke(pred, i_proj, std::forward<T>(t));
};
if (i != ranges::find_if(first1, i, i_cmp, proj1))
continue; // this *i has been checked
if (const auto m {ranges::count_if(first2, last2, i_cmp, proj2)};
m == 0 or m != ranges::count_if(i, last1, i_cmp, proj1))
return false;
}
return true;
}
template<ranges::forward_range R1, ranges::forward_range R2,
class Proj1 = std::identity, class Proj2 = std::identity,
std::indirect_equivalence_relation<
std::projected<ranges::iterator_t<R1>, Proj1>,
std::projected<ranges::iterator_t<R2>, Proj2>>
Pred = ranges::equal_to>
constexpr bool operator()(R1&& r1, R2&& r2, Pred pred = {},
Proj1 proj1 = {}, Proj2 proj2 = {}) const
{
return (*this)(ranges::begin(r1), ranges::end(r1),
ranges::begin(r2), ranges::end(r2),
std::move(pred), std::move(proj1), std::move(proj2));
} };
inline constexpr is_permutation_fn is_permutation {};
π Example π
// Run this code
#include <algorithm>
#include <array>
#include <cmath>
#include <iostream>
#include <ranges>
auto& operator<<(auto& os, std::ranges::forward_range auto const& v)
{
os << "{ ";
for (const auto& e : v)
os << e << ' ';
return os << "}";
}
int main()
{
static constexpr auto r1 = {1, 2, 3, 4, 5};
static constexpr auto r2 = {3, 5, 4, 1, 2};
static constexpr auto r3 = {3, 5, 4, 1, 1};
static_assert(
std::ranges::is_permutation(r1, r1) &&
std::ranges::is_permutation(r1, r2) &&
std::ranges::is_permutation(r2, r1) &&
std::ranges::is_permutation(r1.begin(), r1.end(), r2.begin(), r2.end()));
std::cout
<< std::boolalpha
<< "is_permutation(" << r1 << ", " << r2 << "): "
<< std::ranges::is_permutation(r1, r2) << '\n'
<< "is_permutation(" << r1 << ", " << r3 << "): "
<< std::ranges::is_permutation(r1, r3) << '\n'
<< "is_permutation with custom predicate and projections: "
<< std::ranges::is_permutation(
std::array {-14, -11, -13, -15, -12}, // 1st range
std::array {'F', 'E', 'C', 'B', 'D'}, // 2nd range
[](int x, int y) { return abs(x) == abs(y); }, // predicate
[](int x) { return x + 10; }, // projection for 1st range
[](char y) { return int(y - 'A'); }) // projection for 2nd range
<< '\n';
}
β Output β
is_permutation({ 1 2 3 4 5 }, { 3 5 4 1 2 }): true
is_permutation({ 1 2 3 4 5 }, { 3 5 4 1 1 }): false
is_permutation with custom predicate and projections: true
π See also π
ranges::next_permutation(C++20) (niebloid) β generates the next greater lexicographic permutation of a range of elementsranges::prev_permutation(C++20) (niebloid) β generates the next smaller lexicographic permutation of a range of elementsis_permutation(C++11) (function template) β determines if a sequence is a permutation of another sequencenext_permutation(function template) β generates the next greater lexicographic permutation of a range of elementsprev_permutation(function template) β generates the next smaller lexicographic permutation of a range of elementsequivalence_relation(C++20) (concept) β specifies that a relation imposes an equivalence relation
Signed, sealed, delivered, no cap: http://cppreference.com Β· 2024.06.10 Β· std::ranges..._permutation(3) πβ¨π
π¦ 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