一种约束排列的生成算法

来源:互联网 发布:php数组去除重复 编辑:程序博客网 时间:2024/05/16 09:58

        一种约束排列的生成算法 

                                                                 陈兆斗、柯爱荣

                                                《工程数学学报》

本文所述的约束排列是指:m个非负整数所构成的排列a1a2...am,满足约束条件:a1<=N1,a2<=N2,...,am<=Nm及a1+a2+...+am=M,其中M和N1,N2...  ,Nm是给定的正整数。

算法描述:

算法设计的主要思想来源于p进制整数的进位方法。所不同的是各个位上的进制不同,并有约束条件a1+a2+...+am=M。将上述所有的约束排列记为( |N1,N2...  ,Nm|;M )。其中按照字典排序法,最大排列记为max{ |N1,N2...  ,Nm|;M },最小排列记为min{ |N1,N2...  ,Nm|;M}。下面的数据表是一个具体的约束排列(|3 4 2 3|;9}的所有结果,它们是按字典排序法依照升序给出的。其中最后一列是按照各个排列的次序由小到大所给出的标号:

0  4  2  3                            1
1  3  2  3                            2   
1  4  1  3                            3
1  4  2  2                            4
2  2  2  3                            5
2  3  1  3                            6
2  3  2  2                            7
2  4  0  3                            8
2  4  1  2                            9
2  4  2  1                           10
3  1  2  3                           11
3  2  1  3                           12
3  2  2  2                           13
3  3  0  3                           14
3  3  1  2                           15
3  3  2  1                           16
3  4  0  2                           17
3  4  1  1                           18
3  4  2  0                           19

显然,表中的最大排列为第19号排列3420;最小排列为第1号排列0423。我们对算法的要求是输入其中的任何一个排列,算法按字典排序法自动生成下一个排列,例如:输入第7号排列2322,则应输出第8号排列2403,算法分为以下几个步骤:

1):输入排列a1a2...am,并检验该排列是否满足约束要求。

2):从排列a1a2...am最右边的个位向左依次判断ajaj+1...am是否为约束排列( |Nj,Nj+1...  ,Nm|;Mj ),其中Mj=M-(a1+a2+...+aj-1),j=m,m-1,...,2,1。

3):在上述依次取j=m,m-1,...,2,1的过程中,设k为上述过程中第一个不是约束排列( |Nk,Nk+1...  ,Nm|;Mk )的最大排列的位。即akak+1...am不是( |Nk,Nk+1...  ,Nm|;Mk )的最大排列,而对于i=k+1,k+2,...  ,m,aiai+1...am都是( |Ni,Ni+1...  ,Nm|;Mi )的最大排列。

4):如果k=0则终止。否则,将ak改为ak`=ak+1,取排列ak+1`ak+2`...am`=min{ |Nk+1,Nk+2...  ,Nm|;Mk`},其中Mk`=M-(a1+a2+...+ak-1+ak`)。

5):生成的下一个排列就是:a1a2...akak`ak+1`ak+2`...am`

可以看到在此算法中,最大、最小排列起到重要作用,因为它们的算法都比较简单,在本文中从略。

附:C++实现代码:

 

#include <cstdlib>
#include 
<iostream>

using namespace std;

const int n=4;
int   restraint[n+1]={3,4,2,3,9};
int   list[n];

bool  Is_Max_Permutation( int index ){//index : 头位置, 0表示开始于list[0] 
      int  i, sum=restraint[n];
      
for( i=0; i<index; ++i )
           sum
-=list[i];
      i
=n-1;
      
while( i>=index ){
             
int  j, temp=0, mid;
             
for( j=index; j<i; ++j )
                  temp
+=restraint[j];
             temp
=sum-temp;
             mid
=temp>0? temp : 0;
             
if( mid!=list[i] )
                 
return false;
             sum
-=mid;
             
--i;
      }

      
return true;
}


void  Get_Min_Permutation( int index ){//index : 头位置, 0表示开始于list[0]
      int  i, sum=restraint[n];
      
for( i=0; i<index; ++i )
           sum
-=list[i];
      i
=index;
      
while( i<=n-1 ){
             
int j, temp=0;
             
for( j=n-1; j>i; --j )
                  temp
+=restraint[j];
             temp
=sum-temp;
             list[i]
=temp>0? temp : 0;
             sum
-=list[i];
             
++i;
      }

}
    

bool  Next_Permutation( ){
      
int  i=n-1;
      
while( Is_Max_Permutation( i ) && i>=0 )
             
--i;
      
if( i<0 )
          
return false;
      list[i]
++;
      Get_Min_Permutation( i
+1 );
      
return true;
}

                    
int main(int argc, char *argv[])

    Get_Min_Permutation( 
0 );
    
int  i;
    
do
    
{
      
for( i=0; i<n; ++i )
           cout
<<list[i]<<"  ";
      cout
<<endl;
    }
while( Next_Permutation( ) );
    system(
"PAUSE");
    
return EXIT_SUCCESS;
}