整数m-划分的词典序法生成

整数m-划分的词典序生成 

        

           在“词典序法生成整数划分”一文中,给出了整数划分的词典序生成算法的实现,那里讨论的是无限制的划分。在实际中,经常需要产生划分部分固定的算法,我们不妨称“n的m部分划分”为:“n的m-划分”。例如,产生11的4-划分,即产生:

       8 1 1 1,7 2 1 1,6 3 1 1 ,5 4 1 1,6 2 2 1,5 3 2 1,4 4 2 1,4 3 3 1,5 2 2 2,4 3 2 2,3 3 3 2

         本文给出整数m-划分的词典序算法。这里采用的颠倒的词典序。算法的基本思想是:从高位向低位,找出第一个可增加的位置p,增加之,并使p前面的部分最大化。算法用 C++ 实现如下:

void      m_Partition(  int  n,  int  m ) {
    
if( n<m ) return ;
    
if( m==1 ){
        cout
<<n<<endl;        return ;
    }

    
int *List=new int[m+1], t, s, j;
    List[
0]=n-m+1; List[m]=-1;
    
for( j=1; j<m; ++j )    List[j]=1;
    
while1 ){
        copy( List, List
+m, ostream_iterator<int>(cout," ") ),cout<<endl;
        
if( List[1]<List[0]-1 )
            
--List[0], ++List[1];
        
else{
            j
=2; s=List[0]+List[1]-1;
            
while( List[j]>=List[0]-1 )
                s
+=List[j], ++j;
            
if( j==m ) return ;
            
++List[j]; t=List[j--];
            
while( j>0 )
                List[j
--]=t, s-=t;
            List[
0]=s;
        }
        
    }

    delete []List;
}

               来看一个整数m-划分算法的应用:

           试产生满足a1+a2+a3+a4+a5+a6+a7=175,其中a1>a2>a3>a4>a5>a6>a7>0 的所有组合(a1,a2,a3,a4,a5,a6,a7)。

           怎么求解?第一个想法是:利用整数m-划分的词典序算法,产生175的7-划分,再从结果中剔除那些元素有重复的解。这个想法可以解决问题,但是效率不高:175的7-划分有9,962,502个,而满足条件的只有4,767,088个。下面给出一个算法,它利用整数m-划分算法,直接产生所有满足条件的解。

           显然,直接利用整数m-划分算法是不能解决问题的。为什么不能?因为m-划分中含有重复项。那么,如何避免重复项产生?显然,整数m-划分的每一个解就是一个多重组合,如果我们知道如何将多重组合转化为元素值单一的组合,问题就解决了。转化的方法不止一个,其中一个比较好理解的是:

            设组合(a1,a2,a3,...,am)  其中a1>=a2>=a3>=...>=am>0为n选m的多重组合,那么,它对应于一个n+m选m个的单一组合(b1,b2,b3,...,bm),其中b1>b2>b3>...>bm>0,映射规则为:

                                                          bi=ai+m-i。                                (1)

           上面的转化方法的正确性是显然的,可以很简单地证明。由上面的转化方法,问题可以转化为:

           产生满足:

          a1+a2+a3+a4+a5+a6+a7=154,其中a1>=a2>=a3>=a4>=a5>=a6>=a7>0 的所有组合(a1,a2,a3,a4,a5,a6,a7)。

          为什么可以转化成这样?因为由映射规则(1)知道,映射前后的组合元素之和的差值是m*(m-1)/2,对于m=7来说,差值是21,175-21=154。因此,我们只需要将算法实现中的输出部分按映射规则重写如下:  

for ( j = 0 ; j < m;  ++ j )
        cout
<< List[j] + m - j - 1 << "   " ;
cout
<< endl;

并以n=154,m=7来调用m_Partition( int n, int m ),就可以得到问题的解。

         除了上面的问题外, 许多其他的问题都可以用整数m-划分算法来求解。比如,穷举n阶幻方时, 如果事先知道所有和为幻和的所有n*n选n的组合,那就可以降低搜索量。显然,可以由整数m-划分算法的变形产生元素和为幻和的n*n选n的所有组合:我们只需要在解决上面的问题的算法的基础上,改动初始化操作就可以完成这个任务,而且算法的效率也足够高。如果你用其他的算法来做这件事的话,效率就可能就没这么好了,比如,如果你采用:先用组合生成算法先产生n*n选n的所有组合,再剔除元素和不满足条件的组合 这样的方法,那么,即使你采用速度极快的组合生成算法,效率也是不敢恭维的:对n=7来说,它需要7-8秒,而采用整数m-划分算法的程序,只要大约50毫秒就完成了任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值