zoukankan      html  css  js  c++  java
  • next_permutation

    首先查看stl中相关信息.
    函数原型:

    template<class BidirectionalIterator>
       bool next_permutation(
          BidirectionalIterator _First
          BidirectionalIterator _Last
       );
    template<class BidirectionalIterator, class BinaryPredicate>
       bool next_permutation(
          BidirectionalIterator _First, 
          BidirectionalIterator _Last,
          BinaryPredicate _Comp
       );


    两个重载函数,第二个带谓词参数_Comp,其中只带两个参数的版本,默认谓词函数为"小于".

    返回值:bool类型

    分析next_permutation函数执行过程:

    假设数列 d1,d2,d3,d4……

    范围由[first,last)标记,调用next_permutation使数列逐次增大,这个递增过程按照字典序。例如,在字母表中,abcd的下一单词排列为abdc,但是,有一关键点,如何确定这个下一排列为字典序中的next,而不是next->next->next……

    若当前调用排列到达最大字典序,比如dcba,就返回false,同时重新设置该排列为最小字典序。

    返回为true表示生成下一排列成功。下面着重分析此过程:

    根据标记从后往前比较相邻两数据,若前者小于(默认为小于)后者,标志前者为X1(位置PX)表示将被替换,再次重后往前搜索第一个不小于X1的数据,标记为X2。交换X1,X2,然后把[PX+1,last)标记范围置逆。完成。

    要点:为什么这样就可以保证得到的为最小递增。

    从位置first开始原数列与新数列不同的数据位置是PX,并且新数据为X2。[PX+1,last)总是递减的,[first,PX)没有改变,因为X2>X1,所以不管X2后面怎样排列都比原数列大,反转[PX+1,last)使此子数列(递增)为最小。从而保证的新数列为原数列的字典序排列next。

    明白了这个原理后,看下面例子:

    int main(){
    int a[] = {3,1,2};
    do{
         cout << a[0] << " " << a[1] << " " << a[2] << endl;
    }
    while (next_permutation(a,a+3));
    return 0;
    }

    输出:312/321         因为原数列不是从最小字典排列开始。

    所以要想得到所有全排列

    int a[] = {3,1,2};   change to   int a[] = {1,2,3};

    另外,库中另一函数prev_permutation与next_permutation相反,由原排列得到字典序中上一次最近排列。

    所以

    int main(){
    int a[] = {3,2,1};
    do{
         cout << a[0] << " " << a[1] << " " << a[2] << endl;
    }
    while (prev_permutation(a,a+3));
    return 0;
    }

    才能得到123的所有排列。

    **************************************************************************************************

    **************************************************************************************************

    next_permutation在algorithm头文件里,可以用它来生成全排列。它的源代码加分析如下

    template<class _BidIt> inline
    bool _Next_permutation(_BidIt _First, _BidIt _Last)
    { // permute and test for pure ascending, using operator<

    //-----------------------------------------------
    _DEBUG_RANGE(_First, _Last);
    _BidIt _Next = _Last;
    if (_First == _Last || _First == --_Next)
       return (false);

    //上面这一块是检查边界范围的

    //-----------------------------------------------/

    for (; ; )
       { // find rightmost element smaller than successor
       _BidIt _Next1 = _Next;
       if (_DEBUG_LT(*--_Next, *_Next1))
        { // swap with rightmost element that's smaller, flip suffix
        _BidIt _Mid = _Last;
        for (; !_DEBUG_LT(*_Next, *--_Mid); )
         ;
        std::iter_swap(_Next, _Mid);

    //本身带的注释已经说得很明白,从最右边开始比较两两相邻的元素,直至找到右边比左边大的一对,左边那个

    //就是将要被替换的,再从最右边开始找比这个元素大的第一个,交换他们两个
        std::reverse(_Next1, _Last);

    //交换之后,翻转交换元素的后面的所有元素
        return (true);
        }

       if (_Next == _First)
        { // pure descending, flip all
        std::reverse(_First, _Last);
        return (false);
        }
       }
    }

    关键是确定一个排列的下一个排列是什么,我看着明白却说不明白,于是转贴一段,以下来自http://www.cppblog.com/yindf/archive/2010/02/24/108312.html

    abcd next_permutation -> abdc

    那么,为什么abcd的下一个是abdc而不是acbd呢?

    说简单一点,用 1,2,3,4 代替 a,b,c,d,可以得到:

    原排列                  中间转换               值
    1,2,3,4         3,2,1             ((3 * (3) + 2) * (2) + 1) * (1) = 23
    1,2,4,3         3,2,0             ((3 * (3) + 2) * (2) + 0) * (1) = 22
    1,3,2,4         3,1,1             ((3 * (3) + 1) * (2) + 1) * (1) = 21
    1,3,4,2         3,1,0             ((3 * (3) + 1) * (2) + 0) * (1) = 20
    1,4,3,2         3,0,1             ((3 * (3) + 0) * (2) + 1) * (1) = 19
    .                   .                      .
    .                   .                      .
    .                   .                      .
    4,3,2,1         0,0,0             ((0 * (3) + 0) * (2) + 0) * (1) = 0
                                   |       |      |                       |                    |                   |
                                   |      |                              |                    |
                                   |                                     |


    上面的中间转换指的是:每一个数字后面比当前位数字大的数字的个数。比如:

    1,3,4,2 中,1 后面有(3, 4, 2) 他们都大于1,所以第一位是 3
                                   3 后面有(4, 2), 但只有4大于3,所以第二位是 1
                                   4 后面有(2), 没有比4 大的,所以第三位是 0
                                   最后一位后面肯定没有更大的,所以省略了一个0。

    经过这种转换以后,就得到了一种表示方式(中间转换),这种表达方式和原排列一一对应,可以相互转化。

    仔细观察这种中间表达方式,发现它的第一位只能是(0,1,2,3),第二位只能是(0,1,2),第三位只能是(0,1)。通常,数字是用十进制表示的,计算机中用二进制,但是现在,我用一种特殊的进制来表示数:

    第一位用1进制,第二位用2进制。。。

    于是就得到了这种中间表示方式的十进制值。如:

                                                                  阶                  
                                                |                |                  |
    1,1,0    ---->   ((1 * (3) + 1) * (2) + 0) * (1) = 8

    3,1,0     ---->   ((3 * (3) + 1) * (2) + 0) * (1) = 20

    这样,就可以得到一个十进制数和一个排列之间的一一对应的关系。
    现在排列数和有序的十进制数有了一一对应的关系(通过改变对应关系,可以使十进制数升序)。

  • 相关阅读:
    文件操作
    数据补充
    小数据池
    列表
    基础知识1
    我的Python之旅开始
    正则
    模块
    迭代器与生成器
    文件操作
  • 原文地址:https://www.cnblogs.com/tjuxqcui/p/5033026.html
Copyright © 2011-2022 走看看