# std::ranges::is_permutation

< cpp‎ | algorithm‎ | ranges

定义于头文件 `<algorithm>` |  |
调用签名 |  |
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 = {}); | (1) | (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 = {}); | (2) | (C++20 起)

1) 若存在范围 `[first1, last1)` 中的元素的排列使得该范围等于 `[first2, last2)` （在应用对应的投影 `Proj1` 和 `Proj2` 后，并以 `Pred` 为比较器）则返回 true 。否则返回 false 。
2) 同 (1) ，但以 `r1` 为第一源范围并以 `r2` 为第二源范围，如同以 ranges::begin(r1) 为 `first1` ，以 ranges::end(r1) 为 `last1` ，以 ranges::begin(r2) 为 `first2` ，并以 ranges::end(r2) 为 `last2` 。

此页面上描述的仿函数实体是 niebloid ，即：

- 调用它们中的任何一者时不可指定显式模板实参。
- 它们无一对实参依赖查找可见。
- 在通常无限定查找寻找函数调用运算符左侧的名称找到它们之一时，它禁止实参依赖查找。

实际上，它们能以函数对象，或以某些特殊编译器扩展实现。

### 参数

first1, last1 | - | 第一元素范围
first2, last2 | - | 第二元素范围
r1 | - | 第一元素范围
r2 | - | 第二元素范围
pred | - | 应用到投影后元素的谓词
proj1 | - | 应用到第一范围中元素的投影
proj2 | - | 应用到第一范围中元素的投影

### 返回值

若范围 `[first1, last1)` 为 `[first2, last2)` 的重排列则为 true 。

### 复杂度

至多应用 \(\scriptsize \mathcal{O}(N^2)\)O(N2) 次谓词和每个投影，或若序列已经相等则准确应用 \(\scriptsize N\)N 次，其中 \(\scriptsize N\)N 为 ranges::distance(first1, last1) 。 然而若 ranges::distance(first1, last1) != ranges::distance(first2, last2) ，则不应用谓词和投影。

### 注解

排列关系是等价关系。

`ranges::is_permutation` 能用于按照其名称地测试重排算法（例如排序、打乱、划分）的正确性。若 `x` 为原范围而 `y` 是重排后范围则 std::is_permutation(x, y) == true 表示 `y` 由可能位于其他位置的“相同”元素组成。

### 可能的实现

    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 {
        // 跳过公共前缀
        auto ret = std::ranges::mismatch(first1, last1, first2, last2,
                                         std::ref(pred), std::ref(proj1), std::ref(proj2));
        first1 = ret.in1, first2 = ret.in2;

        // 在剩余部分迭代，统计每个来自 [first1, last1) 中的元素在 [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; // 已检查过此 *i

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

### 示例

    #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 },  // 第一范围
                std::array{ 'F', 'E', 'C', 'B', 'D' },  // 第二范围
                [](int x, int y) { return abs(x) == abs(y); }, // 谓词
                [](int x) { return x + 10; },           // 第一范围的投影
                [](char y) { return int(y - 'A'); })    // 第二范围的投影
            << '\n';
    }

输出：

    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

### 参阅

ranges::next_permutation (C++20) | 产生某个元素范围的按字典序下一个较大的排列 (niebloid)
ranges::prev_permutation (C++20) | 产生某个元素范围的按字典序下一个较小的排列 (niebloid)
is_permutation (C++11) | 判断一个序列是否为另一个序列的排列 (函数模板)
next_permutation | 产生某个元素范围的按字典顺序的下一个较大的排列 (函数模板)
prev_permutation | 产生某个元素范围的按字典顺序的下一个较小的排列 (函数模板)
equivalence_relation (C++20) | 指定 `relation` 施加等价关系 (概念)