为什么next_placement跳过一些排列

Why does next_permutation skip some permutations?

本文关键字:排列 next placement 为什么      更新时间:2023-10-16

为什么这个简单的函数不输出输入的5个字母字符串的所有排列?我认为应该有120个,它只输出90个。

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
// Creates permutation lists for strings
vector<string> createdcombos2(string letters)
{ 
    vector<string> lettercombos;    
    cout << "Letters are: " << letters << endl; //input string
    do 
        lettercombos.push_back(letters);        
    while(next_permutation(letters.begin(), letters.end()));    
    cout <<"Letter combos: " << endl;  //print out permutations 
    for (auto i : lettercombos)
        cout << i << endl;
    cout << endl << lettercombos.size() << endl; //number of permutations
    return lettercombos;
}

int main() 
{
    string letters = "gnary"; 
    vector<string> lettercombos;
    lettercombos = createdcombos2(letters);
}

若要返回循环中的所有排列,直到next_placement返回false,必须在循环开始之前对向量进行排序。next_placement按升序返回排列。因此,如果你从一个未排序的向量开始,它将在一系列排列的中途开始。

std::sort(letters.begin(), letters.end());
do 
    lettercombos.push_back(letters);        
while(next_permutation(letters.begin(), letters.end()));  

您需要对输入进行排序,next_permutation将按字典顺序返回下一个排列。因为输入排列:"gnary">在字典上比《愤怒》这样的排列"更大",所以永远不会达到那些"更小"的排列。

您可以使用std::sort() 对字符串进行排序

由于之前的一些答案状态,如果您想使用std::next_permutationbool返回值来停止迭代,则必须确保从"排序"排列开始。否则,你的周期将提前终止。

但这并不是绝对必要的。

通过std::next_permutation枚举的置换形成了一个没有开始或结束的循环序列,这意味着你可以无限期地调用std::next_permutation,它将一次又一次地循环通过120个置换的相同序列。这意味着你可以从这个循环中的任何排列开始。你只需要记住你的起始排列,并注意这个排列再次出现的那一刻。当你到达原始排列的那一刻,迭代就结束了。在您的情况下,预计需要120个呼叫std::next_permutation

例如,以下代码打印"abcde"集合的所有5字母排列,即使它从一个完全任意的开始

std::string start = "cadeb", current = start;
do
  std::cout << current << std::endl;
while (std::next_permutation(current.begin(), current.end()), current != start);

然而,人们可以注意到,在循环的每次迭代中比较排列比使用std::next_permutation的返回值(它"免费"来自算法内部(更昂贵,因此,如果你对预排序起始排列的解决方案感到满意,那么这确实是一种更有效的方法

或者,如果你知道循环中排列的确切次数(在这种情况下是120(,你可以简单地调用std::next_permutation的次数(正如@Potatoswatter的回答中所建议的(。

next_permutation的返回bool值类似于溢出条件或进位位。当它按字典顺序从最后一个排列回到第一个排列时,它是false。但它仍然在无条件地前进。

如果你知道正好有120个排列,你可以忽略返回值,只是盲目地循环:

for ( int i = 0; i != 120; ++ i ) {
    lettercombos.push_back(letters);        
    next_permutation(letters.begin(), letters.end());
}