跳到主要内容

std::ranges::prev_permutation() 算法

// (1)
constexpr prev_permutation_result<I>
prev_permutation( I first, S last, Comp comp = {}, Proj proj = {} );

// (2)
constexpr prev_permutation_result<ranges::borrowed_iterator_t<R>>
prev_permutation( R&& r, Comp comp = {}, Proj proj = {} );

参数类型是泛型的,并具有以下约束

  • I - std::bidirectional_iterator (双向迭代器)
  • S - std::sentinel_for<I> (I 的哨兵)
  • R - std::bidirectional_range (双向范围)
  • Comp (比较):
    • (1) - std::sortable<I, Comp, Proj> (可排序的)
    • (2) - std::sortable<ranges::iterator_t<R>, Comp, Proj> (可排序的)
  • Proj - (无)

对于所有重载,`Proj` 和 `Comp` 模板参数的默认类型分别为 `std::identity` 和 `ranges::less`。

  • (1) 将范围 [first; last) 转换为前一个排列,其中所有排列的集合根据二元比较函数对象 comp 和投影函数对象 proj 按字典序排序。返回

    • 如果存在这样的“前一个排列”,则返回 { last, true }
    • { last, false } 并将范围转换为(字典序)最后一个排列,如同通过
    ranges::sort(first, last, comp, proj);
    ranges::reverse(first, last);
  • (2)(1),但使用 r1 作为第一个源范围,r2 作为第二个源范围,如同使用 ranges::begin(r1) 作为 first1ranges::end(r1) 作为 last1ranges::begin(r2) 作为 first2,以及 ranges::end(r2) 作为 last2

本页描述的函数类实体是niebloids

参数

first1
last1

要排列的元素范围。

r

要排列的元素范围。

comp (比较函数)

比较函数对象,如果第一个参数小于第二个参数,则返回true如果第一个参数小于第二个,则为 true。

proj (投影函数)

要应用于元素的投影。

返回值

  • (1) 如果新排列在字典序上小于旧排列,则为 ranges::prev_permutation_result<I>{ last, true }。如果已达到第一个排列且范围已重置为最后一个排列,则为 ranges::prev_permutation_result<I>{ last, false }

  • (2)(1),但返回类型为 ranges::prev_permutation_result<ranges::borrowed_iterator_t<R>>

复杂度

给定 Nranges::distance(first, last)

  • (1) 最多 N / 2 次交换。
  • (2) 最多 ranges::distance(r) 次交换。

平均到整个排列序列,典型的实现每次调用大约使用 3 次比较和 1.5 次交换。

异常

迭代器操作或元素交换可能抛出的任何异常。

可能的实现

实现(例如 MSVC STL)可能在迭代器类型满足 contiguous_iterator 且交换其值类型既不调用非平凡特殊成员函数也不调用 ADL 查找的 swap 时启用向量化。

prev_permutation(1) 和 prev_permutation(2)
struct prev_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::prev_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};
auto i {first};
++i;
if (i == last)
return {std::move(i), false};
auto i_last {ranges::next(first, last)};
i = i_last;
--i;
// main "permutating" loop
for (;;)
{
auto i1 {i};
--i;
if (std::invoke(comp, std::invoke(proj, *i1), std::invoke(proj, *i)))
{
auto j {i_last};
while (!std::invoke(comp, std::invoke(proj, *--j), std::invoke(proj, *i)))
;
ranges::iter_swap(i, j);
ranges::reverse(i1, last);
return {std::move(i_last), true};
}
// permutation "space" is exhausted
if (i == first)
{
ranges::reverse(first, 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::prev_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 prev_permutation_fn prev_permutation {};

示例

Main.cpp
#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 {"cba"};
do print(s);
while (std::ranges::prev_permutation(s.begin(), s.end()).found);

std::cout << "\nGenerate all permutations (range case):\n";
std::array a {'c', 'b', 'a'};
do print(a);
while (std::ranges::prev_permutation(a).found);

std::cout << "\nGenerate all permutations using comparator:\n";
using namespace std::literals;
std::array z { "▁"s, "▄"s, "█"s };
do print(z);
while (std::ranges::prev_permutation(z, std::greater()).found);

std::cout << "\nGenerate all permutations using projection:\n";
std::array<S, 3> r { S{'C',1}, S{'B',2}, S{'A',3} };
do print(r, '\n');
while (std::ranges::prev_permutation(r, {}, &S::c).found);
}
可能的输出
Generate all permutations (iterators case):
{ c b a } { c a b } { b c a } { b a c } { a c b } { a b c }
Generate all permutations (range case):
{ c b a } { c a b } { b c a } { b a c } { a c b } { a b c }
Generate all permutations using comparator:
{ ▁ ▄ █ } { ▁ █ ▄ } { ▄ ▁ █ } { ▄ █ ▁ } { █ ▁ ▄ } { █ ▄ ▁ }
Generate all permutations using projection:
{ {'C', 1} {'B', 2} {'A', 3} }
{ {'C', 1} {'A', 3} {'B', 2} }
{ {'B', 2} {'C', 1} {'A', 3} }
{ {'B', 2} {'A', 3} {'C', 1} }
{ {'A', 3} {'C', 1} {'B', 2} }
{ {'A', 3} {'B', 2} {'C', 1} }
本文源自 此 CppReference 页面。它可能为了改进或编辑偏好而进行了修改。点击“编辑此页面”查看此文档的所有更改。
悬停查看原始许可证。

std::ranges::prev_permutation() 算法

// (1)
constexpr prev_permutation_result<I>
prev_permutation( I first, S last, Comp comp = {}, Proj proj = {} );

// (2)
constexpr prev_permutation_result<ranges::borrowed_iterator_t<R>>
prev_permutation( R&& r, Comp comp = {}, Proj proj = {} );

参数类型是泛型的,并具有以下约束

  • I - std::bidirectional_iterator (双向迭代器)
  • S - std::sentinel_for<I> (I 的哨兵)
  • R - std::bidirectional_range (双向范围)
  • Comp (比较):
    • (1) - std::sortable<I, Comp, Proj> (可排序的)
    • (2) - std::sortable<ranges::iterator_t<R>, Comp, Proj> (可排序的)
  • Proj - (无)

对于所有重载,`Proj` 和 `Comp` 模板参数的默认类型分别为 `std::identity` 和 `ranges::less`。

  • (1) 将范围 [first; last) 转换为前一个排列,其中所有排列的集合根据二元比较函数对象 comp 和投影函数对象 proj 按字典序排序。返回

    • 如果存在这样的“前一个排列”,则返回 { last, true }
    • { last, false } 并将范围转换为(字典序)最后一个排列,如同通过
    ranges::sort(first, last, comp, proj);
    ranges::reverse(first, last);
  • (2)(1),但使用 r1 作为第一个源范围,r2 作为第二个源范围,如同使用 ranges::begin(r1) 作为 first1ranges::end(r1) 作为 last1ranges::begin(r2) 作为 first2,以及 ranges::end(r2) 作为 last2

本页描述的函数类实体是niebloids

参数

first1
last1

要排列的元素范围。

r

要排列的元素范围。

comp (比较函数)

比较函数对象,如果第一个参数小于第二个参数,则返回true如果第一个参数小于第二个,则为 true。

proj (投影函数)

要应用于元素的投影。

返回值

  • (1) 如果新排列在字典序上小于旧排列,则为 ranges::prev_permutation_result<I>{ last, true }。如果已达到第一个排列且范围已重置为最后一个排列,则为 ranges::prev_permutation_result<I>{ last, false }

  • (2)(1),但返回类型为 ranges::prev_permutation_result<ranges::borrowed_iterator_t<R>>

复杂度

给定 Nranges::distance(first, last)

  • (1) 最多 N / 2 次交换。
  • (2) 最多 ranges::distance(r) 次交换。

平均到整个排列序列,典型的实现每次调用大约使用 3 次比较和 1.5 次交换。

异常

迭代器操作或元素交换可能抛出的任何异常。

可能的实现

实现(例如 MSVC STL)可能在迭代器类型满足 contiguous_iterator 且交换其值类型既不调用非平凡特殊成员函数也不调用 ADL 查找的 swap 时启用向量化。

prev_permutation(1) 和 prev_permutation(2)
struct prev_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::prev_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};
auto i {first};
++i;
if (i == last)
return {std::move(i), false};
auto i_last {ranges::next(first, last)};
i = i_last;
--i;
// main "permutating" loop
for (;;)
{
auto i1 {i};
--i;
if (std::invoke(comp, std::invoke(proj, *i1), std::invoke(proj, *i)))
{
auto j {i_last};
while (!std::invoke(comp, std::invoke(proj, *--j), std::invoke(proj, *i)))
;
ranges::iter_swap(i, j);
ranges::reverse(i1, last);
return {std::move(i_last), true};
}
// permutation "space" is exhausted
if (i == first)
{
ranges::reverse(first, 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::prev_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 prev_permutation_fn prev_permutation {};

示例

Main.cpp
#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 {"cba"};
do print(s);
while (std::ranges::prev_permutation(s.begin(), s.end()).found);

std::cout << "\nGenerate all permutations (range case):\n";
std::array a {'c', 'b', 'a'};
do print(a);
while (std::ranges::prev_permutation(a).found);

std::cout << "\nGenerate all permutations using comparator:\n";
using namespace std::literals;
std::array z { "▁"s, "▄"s, "█"s };
do print(z);
while (std::ranges::prev_permutation(z, std::greater()).found);

std::cout << "\nGenerate all permutations using projection:\n";
std::array<S, 3> r { S{'C',1}, S{'B',2}, S{'A',3} };
do print(r, '\n');
while (std::ranges::prev_permutation(r, {}, &S::c).found);
}
可能的输出
Generate all permutations (iterators case):
{ c b a } { c a b } { b c a } { b a c } { a c b } { a b c }
Generate all permutations (range case):
{ c b a } { c a b } { b c a } { b a c } { a c b } { a b c }
Generate all permutations using comparator:
{ ▁ ▄ █ } { ▁ █ ▄ } { ▄ ▁ █ } { ▄ █ ▁ } { █ ▁ ▄ } { █ ▄ ▁ }
Generate all permutations using projection:
{ {'C', 1} {'B', 2} {'A', 3} }
{ {'C', 1} {'A', 3} {'B', 2} }
{ {'B', 2} {'C', 1} {'A', 3} }
{ {'B', 2} {'A', 3} {'C', 1} }
{ {'A', 3} {'C', 1} {'B', 2} }
{ {'A', 3} {'B', 2} {'C', 1} }
本文源自 此 CppReference 页面。它可能为了改进或编辑偏好而进行了修改。点击“编辑此页面”查看此文档的所有更改。
悬停查看原始许可证。