整数分拆的非递归程序

我都不记得这是多少年前写的代码了,当时写完感觉很得意,现在回头看看,用了太多的 ++ 自增写法,自己都晕了。但是我也懒得再修改了。

这个程序实现的是打印给定正整数 $n$ 的全部分拆,重要的是它不是递归的,内存占用上仅使用一个长度为 $n$ 的数组(还有几个额外变量)。

这个方法在 Knuth 的计算机程序设计艺术的第四卷组合算法中有讲。

 

首先是使用逆字典序打印,即先输出 $n$,最后输出 $(1,1,\ldots,1)$。

 

算法的思路很简单,要输出给定正整数 $n$ 的全部分拆,在一个长度是 $n$ 的数组上操作就行。为了方便起见我们申请一个长度是 $n+1$ 的数组 $a$,$a_0$ 就不管它了,只在 $a_1$ 到 $a_n$ 上操作。

这里的关键是追踪两个下标 $h,m$ 的变化情况。其中 $a_h$ 是数组从右往左数第一个大于 1 的元素,$m$ 是当前分拆的长度。

比如对分拆 $(5,4,3,1,1,1)$,有 $h=3$ 和 $m=6$。

要从当前的分拆跳跃到逆字典序下的下一个分拆,你需要考虑 $a_h$ 是否等于 2:

  1. 如果 $a_h=2$,那么下一个分拆应该是 $\cdots a_{h-1}11\cdots1$,然后把指标 $h$ 左移一个位置,数组长度 $m$ 增加 1;
  2. 如果 $a_h=x+1>2$,那么下一个分拆应该形如 $\cdots xx\cdots xt$。这种情形下 $h,m$ 的变化情况略复杂,要考虑 $t$ 是 0,1 或者大于 1 三种可能。详情见程序。

 

#include<stdio.h>
void print_reverse_partition(int n)
{ int i=1;
  int m=1;
  int h=1;
  int t,r;
  int a[n+1];
  for(;i<n+1;++i)a[i]=1;
  a[1]=n;
  printf("%d\n",a[1]);

  while(a[1]!=1)
       {  if(a[h]==2) {a[h--]--;m++;}
               else { r=--a[h];
                          t=m-h+1;
                         while(t>=r){a[++h]=r;t-=r;}
                         if(t==0)m=h;
                         else m=h+1;
                         if(t>=2) {a[++h]=t;}
                    }
                for(i=1;i<m+1;i++)
                printf("%d ",a[i]);
                printf("\n");
        }
}

int main()
{  int n;
   printf("input the number:\n");
   scanf("%d",&n);
   printf("now output all the partitions of %d:\n",n);
   print_reverse_partition(n);
   return 0;
}
View Code

 

接下来使用字典序打印:

 

#include<stdio.h>

void print_partition(int n)
{  int a[n+1],i,j,m,h,r;
   for(i=1;i<=n;i++){
         a[i]=1;
         printf("%d ",a[i]);
                     }
   printf("\n");
   a[0]=-1,a[1]=2,h=1,m=n-1;
   for(i=1;i<=m;i++){
         printf("%d ",a[i]);
                   }
   printf("\n");
   while(a[1]!=n){   if  ( m-h>1)
                         { a[++h]=2; m--;}
                   else  { j=m-2;
                           while (a[j]==a[m-1]) a[j--]=1;
                           h=j+1;
                           a[h]=a[m-1]+1;
                           r=a[m]+a[m-1]*(m-h-1);
                           a[m]=1;
                           if (m-h>>1) a[m-1]=1;
                           m=h+r-1;
                         }     
                    for (i=1;i<=m;i++)
                    printf("%d ",a[i]);
                    printf("\n");
                 }
}

int main()
{  int n;
    printf("input the number:\n");
    scanf("%d",&n);
    printf("now output all the partitions of %d:\n",n);
    print_partition(n);
    return 0;
}
View Code

 

  

最后是只打印固定长度的分拆:

 

#include<stdio.h>

unsigned long int PartOfFixedLength(int n, int k)
{
     if ((n<=0) || (k<=0)) {
          printf("Error: Arguments must be positive integers!\n");
          return 0;        }

     int a[k+2],i;
     a[1]=n-k+1,a[k+1]=0;
     for(i=2; i<=k; ++i) a[i]=1;
     printf("Line 1: ");
     for(i=1; i<=k; ++i)
     printf(" %d ",a[i]);
     printf("\n");
      
     unsigned long int TotalNumbers = 1;
     int s,x,j;
     int n_div_k = n/k;
     int n_mod_k = n%k;
     int q = n_div_k + ((n_mod_k == 0)?0:1);
     while (a[1]!=q) {
            j=2;
            s=a[1]-1;
            while(a[j]>=a[1]-1)
                  s+=a[j++];
            if (j<=k)  { 
                x=++a[j--];
                while(j>1) {
                      a[j--]=x;
                      s-=x;}
                a[1]=s;
                       }

           TotalNumbers++;
           printf("Line %lu: ",TotalNumbers);
           for(i=1;i<=k;i++)
           printf(" %d ",a[i]);
           printf("\n");
                     }
     return TotalNumbers;           
}            
View Code

 

你可能感兴趣的:(整数分拆的非递归程序)