Difference between revisions of "cpp/algorithm/ranges/is permutation"
(created page for ranges::is_permutation; WIP) |
m (fmt, wording.) |
||
(14 intermediate revisions by 4 users not shown) | |||
Line 2: | Line 2: | ||
{{cpp/algorithm/ranges/navbar}} | {{cpp/algorithm/ranges/navbar}} | ||
{{dcl begin}} | {{dcl begin}} | ||
− | {{dcl header | algorithm}} | + | {{dcl header|algorithm}} |
{{dcl h|Call signature}} | {{dcl h|Call signature}} | ||
− | {{dcl | num=1 | since=c++20 |1= | + | {{dcl|num=1|since=c++20|1= |
− | template<std::forward_iterator I1, std::sentinel_for<I1> S1, | + | 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 | + | constexpr bool |
− | + | is_permutation( I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, | |
+ | Proj1 proj1 = {}, Proj2 proj2 = {} ); | ||
}} | }} | ||
− | {{dcl | num=2 | since=c++20 |1= | + | {{dcl|num=2|since=c++20|1= |
− | template< | + | 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>> | |
− | constexpr bool | + | Pred = ranges::equal_to > |
− | + | constexpr bool | |
+ | is_permutation( R1&& r1, R2&& r2, Pred pred = {}, | ||
+ | Proj1 proj1 = {}, Proj2 proj2 = {} ); | ||
}} | }} | ||
{{dcl end}} | {{dcl end}} | ||
− | @1@ Returns {{c|true}} if there exists a permutation of the elements in range {{ | + | @1@ Returns {{c|true}} if there exists a {{enwiki|permutation}} of the elements in range {{range|first1|last1}} that makes the range ''equal'' to {{range|first2|last2}} (after application of corresponding projections {{c|Proj1}}, {{c|Proj2}}, and using the binary predicate {{c|Pred}} as a comparator). Otherwise returns {{c|false}}. |
− | @2@ Same as {{v|1}}, but uses {{ | + | @2@ Same as {{v|1}}, but uses {{c|r1}} as the first source range and {{c|r2}} as the second source range, as if using {{c|ranges::begin(r1)}} as {{c|first1}}, {{c|ranges::end(r1)}} as {{c|last1}}, {{c|ranges::begin(r2)}} as {{c|first2}}, and {{c|ranges::end(r2)}} as {{c|last2}}. |
{{cpp/ranges/niebloid}} | {{cpp/ranges/niebloid}} | ||
Line 33: | Line 36: | ||
===Parameters=== | ===Parameters=== | ||
{{par begin}} | {{par begin}} | ||
− | {{par | first1, last1 | the first range of the elements}} | + | {{par|first1, last1|the first range of the elements}} |
− | {{par | first2, last2 | the second range of the elements}} | + | {{par|first2, last2|the second range of the elements}} |
− | {{par | r1 | the first range of the elements}} | + | {{par|r1|the first range of the elements}} |
− | {{par | r2 | the second range of the elements}} | + | {{par|r2|the second range of the elements}} |
− | {{par | pred | predicate to apply to the projected elements}} | + | {{par|pred|predicate to apply to the projected elements}} |
− | {{par | proj1 | projection to apply to the elements in the first range}} | + | {{par|proj1|projection to apply to the elements in the first range}} |
− | {{par | proj2 | projection to apply to the elements in the second range}} | + | {{par|proj2|projection to apply to the elements in the second range}} |
{{par end}} | {{par end}} | ||
===Return value=== | ===Return value=== | ||
− | {{c|true}} if the range {{ | + | {{c|true}} if the range {{range|first1|last1}} is a permutation of the range {{range|first2|last2}}. |
===Complexity=== | ===Complexity=== | ||
− | At most {{ | + | At most {{mathjax-or|\(\scriptsize \mathcal{O}(N^2)\)|O(N<sup>2</sup>)}} applications of the predicate and each projection, or exactly {{mathjax-or|\(\scriptsize N\)|N}} if the sequences are already equal, where {{mathjax-or|\(\scriptsize N\)|N}} is {{c|ranges::distance(first1, last1)}}. |
− | However if {{c| | + | However if {{c|1=ranges::distance(first1, last1) != ranges::distance(first2, last2)}}, no applications of the predicate and projections are made. |
− | + | ===Notes=== | |
+ | The ''permutation'' relation is an {{enwiki|equivalence relation}}. | ||
− | + | The {{tt|ranges::is_permutation}} can be used in testing, e.g. to check the correctness of rearranging algorithms such as sorting, shuffling, partitioning. If {{tt|p}} is an original sequence and {{tt|q}} is a "mutated" sequence, then {{c|1=ranges::is_permutation(p, q) == true}} means that {{tt|q}} consist of "the same" elements (maybe permuted) as {{tt|p}}. | |
− | {{eq fun| 1= | + | |
− | + | ===Possible implementation=== | |
− | }} | + | {{eq fun|1= |
+ | 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=== | ===Example=== | ||
− | {{example|code= | + | {{example |
+ | |code= | ||
#include <algorithm> | #include <algorithm> | ||
#include <array> | #include <array> | ||
#include <cmath> | #include <cmath> | ||
#include <iostream> | #include <iostream> | ||
+ | #include <ranges> | ||
− | auto& operator<< (auto& os, auto const& v) { | + | auto& operator<<(auto& os, std::ranges::forward_range auto const& v) |
+ | { | ||
os << "{ "; | os << "{ "; | ||
− | for ( | + | for (const auto& e : v) |
+ | os << e << ' '; | ||
return os << "}"; | return os << "}"; | ||
} | } | ||
Line 71: | Line 132: | ||
int main() | int main() | ||
{ | { | ||
− | static constexpr auto r1 = {1,2,3,4,5}; | + | static constexpr auto r1 = {1, 2, 3, 4, 5}; |
− | static constexpr auto r2 = {3,5,4,1,2}; | + | static constexpr auto r2 = {3, 5, 4, 1, 2}; |
− | static constexpr auto r3 = {3,5,4,1,1}; | + | static constexpr auto r3 = {3, 5, 4, 1, 1}; |
static_assert( | static_assert( | ||
Line 79: | Line 140: | ||
std::ranges::is_permutation(r1, r2) && | std::ranges::is_permutation(r1, r2) && | ||
std::ranges::is_permutation(r2, r1) && | std::ranges::is_permutation(r2, r1) && | ||
− | std::ranges::is_permutation(r1.begin(), r1.end(), r2.begin(), r2.end()) | + | std::ranges::is_permutation(r1.begin(), r1.end(), r2.begin(), r2.end())); |
− | + | ||
std::cout | std::cout | ||
<< std::boolalpha | << std::boolalpha | ||
− | << "is_permutation( " << r1 << ", " << r2 << " ): " | + | << "is_permutation(" << r1 << ", " << r2 << "): " |
<< std::ranges::is_permutation(r1, r2) << '\n' | << std::ranges::is_permutation(r1, r2) << '\n' | ||
− | << "is_permutation( " << r1 << ", " << r3 << " ): " | + | << "is_permutation(" << r1 << ", " << r3 << "): " |
<< std::ranges::is_permutation(r1, r3) << '\n' | << std::ranges::is_permutation(r1, r3) << '\n' | ||
<< "is_permutation with custom predicate and projections: " | << "is_permutation with custom predicate and projections: " | ||
<< std::ranges::is_permutation( | << std::ranges::is_permutation( | ||
− | std::array{ -14, -11, -13, -15, -12 }, // 1st range | + | std::array {-14, -11, -13, -15, -12}, // 1st range |
− | std::array{ 'F', 'E', 'C', 'B', 'D' }, // 2nd 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'; | << '\n'; | ||
} | } | ||
− | | output= | + | |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 2 }): true |
− | is_permutation( { 1 2 3 4 5 }, { 3 5 4 1 1 } ): false | + | is_permutation({ 1 2 3 4 5 }, { 3 5 4 1 1 }): false |
is_permutation with custom predicate and projections: true | is_permutation with custom predicate and projections: true | ||
}} | }} | ||
− | ===See | + | ===See also=== |
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc inc | cpp/algorithm/ranges/dsc next_permutation}} | + | {{dsc inc|cpp/algorithm/ranges/dsc next_permutation}} |
− | {{dsc inc | cpp/algorithm/ranges/dsc prev_permutation}} | + | {{dsc inc|cpp/algorithm/ranges/dsc prev_permutation}} |
− | {{dsc inc | cpp/algorithm/dsc is_permutation}} | + | {{dsc inc|cpp/algorithm/dsc is_permutation}} |
− | {{dsc inc | cpp/algorithm/dsc next_permutation}} | + | {{dsc inc|cpp/algorithm/dsc next_permutation}} |
− | {{dsc inc | cpp/algorithm/dsc prev_permutation}} | + | {{dsc inc|cpp/algorithm/dsc prev_permutation}} |
+ | {{dsc inc|cpp/concepts/dsc equivalence_relation}} | ||
{{dsc end}} | {{dsc end}} | ||
{{langlinks|de|es|fr|it|ja|pt|ru|zh}} | {{langlinks|de|es|fr|it|ja|pt|ru|zh}} |
Latest revision as of 12:51, 23 April 2024
Defined in header <algorithm>
|
||
Call signature |
||
template< std::forward_iterator I1, std::sentinel_for<I1> S1, std::forward_iterator I2, std::sentinel_for<I2> S2, |
(1) | (since C++20) |
template< ranges::forward_range R1, ranges::forward_range R2, class Proj1 = std::identity, class Proj2 = std::identity, |
(2) | (since C++20) |
[
first1,
last1)
that makes the range equal to [
first2,
last2)
(after application of corresponding projections Proj1, Proj2, and using the binary predicate Pred as a comparator). Otherwise returns false.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.
Contents |
[edit] Parameters
first1, last1 | - | the first range of the elements |
first2, last2 | - | the second range of the elements |
r1 | - | the first range of the elements |
r2 | - | the second range of the elements |
pred | - | predicate to apply to the projected elements |
proj1 | - | projection to apply to the elements in the first range |
proj2 | - | projection to apply to the elements in the second range |
[edit] Return value
true if the range [
first1,
last1)
is a permutation of the range [
first2,
last2)
.
[edit] Complexity
At most O(N2) applications of the predicate and each projection, or exactly N if the sequences are already equal, where N is ranges::distance(first1, last1). However if ranges::distance(first1, last1) != ranges::distance(first2, last2), no applications of the predicate and projections are made.
[edit] Notes
The permutation relation is an equivalence relation.
The 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
consist of "the same" elements (maybe permuted) as p
.
[edit] 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 {}; |
[edit] Example
#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
[edit] See also
(C++20) |
generates the next greater lexicographic permutation of a range of elements (niebloid) |
(C++20) |
generates the next smaller lexicographic permutation of a range of elements (niebloid) |
(C++11) |
determines if a sequence is a permutation of another sequence (function template) |
generates the next greater lexicographic permutation of a range of elements (function template) | |
generates the next smaller lexicographic permutation of a range of elements (function template) | |
(C++20) |
specifies that a relation imposes an equivalence relation (concept) |