Difference between revisions of "cpp/algorithm/ranges/is permutation"
m (+{{enwiki|permutation}}) |
Andreas Krug (Talk | contribs) m (fmt) |
||
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, | std::forward_iterator I2, std::sentinel_for<I2> S2, | ||
Line 10: | Line 10: | ||
std::indirect_equivalence_relation<std::projected<I1, Proj1>, | std::indirect_equivalence_relation<std::projected<I1, Proj1>, | ||
std::projected<I2, Proj2>> | std::projected<I2, Proj2>> | ||
− | + | Pred = ranges::equal_to > | |
constexpr bool is_permutation( I1 first1, S1 last1, I2 first2, S2 last2, | constexpr bool is_permutation( I1 first1, S1 last1, I2 first2, S2 last2, | ||
Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {} ); | Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {} ); | ||
}} | }} | ||
− | {{dcl | num=2 | since=c++20 |1= | + | {{dcl|num=2|since=c++20|1= |
template< ranges::forward_range R1, ranges::forward_range R2, | 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 = {}, | constexpr bool is_permutation( R1&& r1, R2&& r2, Pred pred = {}, | ||
Proj1 proj1 = {}, Proj2 proj2 = {} ); | Proj1 proj1 = {}, Proj2 proj2 = {} ); | ||
Line 25: | Line 25: | ||
{{dcl end}} | {{dcl end}} | ||
− | @1@ Returns {{c|true}} if there exists a {{enwiki|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 33: | ||
===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=== | ||
Line 55: | Line 55: | ||
===Possible implementation=== | ===Possible implementation=== | ||
− | {{eq fun| 1= | + | {{eq fun|1= |
struct is_permutation_fn | 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 | + | { |
− | + | // 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) }; | + | { |
− | + | const auto i_proj {std::invoke(proj1, *i)}; | |
− | return std::invoke(pred, i_proj, std::forward<T>(t)); | + | 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), | return (*this)(ranges::begin(r1), ranges::end(r1), | ||
ranges::begin(r2), ranges::end(r2), | ranges::begin(r2), ranges::end(r2), | ||
std::move(pred), std::move(proj1), std::move(proj2)); | std::move(pred), std::move(proj1), std::move(proj2)); | ||
− | + | } | |
}; | }; | ||
− | inline constexpr is_permutation_fn is_permutation{}; | + | inline constexpr is_permutation_fn is_permutation {}; |
}} | }} | ||
Line 113: | Line 117: | ||
#include <ranges> | #include <ranges> | ||
− | auto& operator<< (auto& os, std::ranges::forward_range auto const& v) { | + | auto& operator<<(auto& os, std::ranges::forward_range auto const& v) |
+ | { | ||
os << "{ "; | os << "{ "; | ||
for (auto const& e : v) os << e << ' '; | for (auto const& e : v) os << e << ' '; | ||
Line 121: | Line 126: | ||
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 129: | Line 134: | ||
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 | ||
Line 141: | Line 145: | ||
<< "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, int y) { return abs(x) == abs(y); }, // predicate | ||
− | [](int x) { return x + 10; }, | + | [](int x) { return x + 10; }, // projection for 1st range |
− | [](char y) { return int(y - 'A'); }) | + | [](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 | ||
Line 156: | Line 160: | ||
===See also=== | ===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 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}} |
Revision as of 10:41, 12 April 2023
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 |
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 |
Return value
true if the range [
first1,
last1)
is a permutation of the range [
first2,
last2)
.
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.
Notes
The permutation relation is an equivalence relation.
The ranges::is_permutation can be used in testing, namely to check the correctness of rearranging algorithms (e.g. sorting, shuffling, partitioning). If x
is an original range and y
is a permuted range then ranges::is_permutation(x, y) == true means that y
consist of "the same" elements, maybe staying at other positions.
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
#include <algorithm> #include <array> #include <cmath> #include <iostream> #include <ranges> auto& operator<<(auto& os, std::ranges::forward_range auto const& v) { os << "{ "; for (auto const& 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
(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) |