整数划分问题

来源:互联网 发布:菲律宾禁毒知乎 编辑:程序博客网 时间:2024/05/29 18:26

                                                                                 整数划分问题

         正整数s(简称为和数)的划分(又称分划或拆分)是把s分成为若干个正整数(简称为零数或部分)之和,划分式中允许零数重复,且不记零数的次序。

试求s=12共有多少个不同的划分式?展示出所有这些划分式。

1  整数划分递推设计

1.探索划分的递推关系

为了建立递推关系,先对和数k较小时的划分式作观察归纳:

k=2:1+1;2

k=3:1+1+1;1+2;3

k=4:1+1+1+1;1+1+2;1+3;2+2;4

k=5:1+1+1+1+1;1+1+1+2;1+1+3;1+2+2;1+4;2+3;5

由以上各划分看到,除和数本身k=k这一特殊划分式外,其它每一个划分式至少为两项之和。约定在所有划分式中零数作不减排列,探索和数k的划分式与和数k−1的划分式存在以下递推关系:

(1)在所有和数k−1的划分式前加零数1都是和数k的划分式。

(2)和数k−1的划分式的前两个零数作比较,如果第1个零数x1小于第2个零数x2,则把第1个零数加1后成为和数k的划分式。

2.递推算法设计

    设置三维数组a,a(k, j, i)为和数k的第j个划分式的第i个数。

    从k=2开始,显然递推的初始条件为:

   a(2,1,1)=1;a(2,1,2)=1; a(2,2,1)=2。

   根据递推关系,实施递推:

(1)实施在k−1所有划分式前加1操作

        a(k,j,1)=1; 

       for(t=2;t<=k;t++)          

      a(k,j,t)=a(k−1,j,t−1);    // k−1的第t−1项变为k的第t项    

(2)若k−1划分式第1项小于第2项,第1项加1,变为k的第i个划分式

  if(a(k−1,j,1)<a(k−1,j,2)

    { a(k,i,1)=a(k−1,j,1)+1;    

      for(t=2;t<=k−1;t++)

         a(k,i,t)=a(k−1,j,t);

    }

以上递推算法的时间复杂度与空间复杂度为O(n2u),其中u为n划分式个数。注意到u随n增加非常快,难以估算其数量级,其时间复杂度与空间复杂度是很高的。

3.整数划分的程序实现

// 整数s划分展示  

#include <stdio.h>

void main()

{ int s,i,j,k,t,u; 

  static int a[21][800][21];

  printf("input s(s<=20):"); scanf("%d",&s);

  a[2][1][1]=1;a[2][1][2]=1;a[2][2][1]=2;

  u=2;

  for(k=3;k<=s;k++)

    { for(j=1;j<=u;j++)

       { a[k][j][1]=1;           

         for(t=2;t<=k;t++)         // 实施在k−1所有划分式前加1操作  

            a[k][j][t]=a[k−1][j][t−1];    

       }

     for(i=u,j=1;j<=u;j++)

       if(a[k−1][j][1]<a[k−1][j][2])   // 若k−1划分式第1项小于第2项  

         { i++;               // 第1项加1为k的第i个划分式的第1项  

          a[k][i][1]=a[k−1][j][1]+1;     

          for(t=2;t<=k−1;t++)

             a[k][i][t]=a[k−1][j][t];

          }

     i++;a[k][i][1]=k;              // k的最后一个划分式为:k=k  

     u=i;

    }

  for(j=1;j<=u;j++)                // 输出s的所有划分式  

    { printf("%3d: %d=%d",j,s,a[s][j][1]);

      i=2;

      while(a[s][j][i]>0)

        {printf("+%d",a[s][j][i]);i++;}

      printf("\n");

     }

 }

运行程序,输入s=12,得

input s(s<=20):12

  1: 12=1+1+1+1+1+1+1+1+1+1+1+1

  2: 12=1+1+1+1+1+1+1+1+1+1+2

  3: 12=1+1+1+1+1+1+1+1+1+3

  ……

 75: 12=5+7

 76: 12=6+6

 77: 12=12

运行程序,输入s=20,可得20的共627个划分式。

2  整数划分递推优化

         考察以上应用三维数组a(k, j, i)完成递推过程,当由k−1的划分式推出k的划分式时,k−1以前的数组单元已完全闲置。为此可考虑把三维数组a(k,j,i)改进为二维数组a(j,i)。二维数组a(j,i)表示和数是k−1的已有划分式,根据递推关系推出k的划分式:

1. 把a(j,i)依次存储到a(j,i+1),加上第一项a(j,1)=1;这样完成在k−1的所有划分式前加1的操作,转化为k的划分式。

for(t=i;t>=1;t−−) 

   a(j,t+1)=a(j,t);

a(j,1)=1; 

2.  对已转化的u个划分式逐个检验,若其第2个数小于第3个数(相当于k−1时的第1个数小于第2个数),则把第2个数加1,去除第一个数后,作为k时增加的一个划分式,为第t(t从u开始,每增加一个划分式,t增1)划分式。

for(t=u,j=1;j<=u;j++)

  if(a(j,2)<a(j,3))     // 若k−1划分式第1项小于第2项  

    {t++;

     a(t,1)=a(j,2)+1;  // 第1项加1 作为k的第t个划分式的第1项 

     i=3;

     while(a(j,i)>0)

        {a(t,i−1)=a(j,i);i++;}

     }    

改进的递推设计把原有的三维数组优化为二维数组,降低了算法的空间复杂度,拓展了算法的求解范围。

3.优化递推设计的程序实现

// 整数s划分优化递推设计  

#include <stdio.h>

void main()

{ int s,i,j,k,t,u; 

  static int a[1600][25];

  printf("input s(s<=24):"); 

  scanf("%d",&s);

  a[1][1]=1;a[1][2]=1;a[2][1]=2;u=2;

  for(k=3;k<=s;k++)

    { for(j=1;j<=u;j++)

        { i=k−1;           

          for(t=i;t>=1;t−−)        // 实施在k−1所有划分式前加1操作  

             a[j][t+1]=a[j][t];//把k-1的所有划分式都可看做有k-1项,其中在有些划分式的k-1项中有很多0项

          a[j][1]=1;

        }

      for(t=u,j=1;j<=u;j++)

        if(a[j][2]<a[j][3])          // 若k−1划分式第1项小于第2项  

          { t++;

            a[t][1]=a[j][2]+1;    // 第1项加1  

            i=3;

            while(a[j][i]>0)

               {a[t][i−1]=a[j][i];i++;}

          }

      t++;a[t][1]=k;                // 最后一个划分式为:k=k  

      u=t;

     }

  for(j=1;j<=u;j++)                // 输出所有u个划分式  

    { printf("%3d: %d=%d",j,s,a[j][1]);

      i=2;

      while(a[j][i]>0)

         {printf("+%d",a[j][i]);i++;}

      printf("\n");

     }

  }

注意:因划分式的数量u随和数s增加相当迅速,尽管改进为二维数组,求解的和数s不可能太大。

0 0
原创粉丝点击