自然数的所有拆分

任何一个大于 1的自然数 n,总可以拆分成若干个小于 n的自然数之和,试求 n的所有拆分。

用不完全归纳法

n =2 可拆分成 2 =1 +1

n =3 可拆分成 3 =1 +2 =1 +1 +1

n =4 可拆分成 4 =1 +3 =1 +1 +2 =1 +1 +1 +1 =2 +2

……

n =7 可拆分成 7 =1 +6

                           =1 +1 +5

                           =1 +1 +1 +4

                           =1 +1 +1 +1 +3

                            =1 +1 +1 +1 +1 +2

                            =1 +1 +1 +1 +1 +1 +1

                            =1 +1 +1 +2 +2

                           =1 +1 +2 +3

                           =1 +2 +4

                           =1 +2 +2 +2

                           =1 +3 +3

                           =2 +5

                           =2 +2 +3

                           =3 +4

方法1:递归

用数组 a 存储完成 n 的一种拆分。从上面不完全归纳法的分析 n =7 时,按 a[1]分类,有a[1]=1,a[1]= 2,…,a[1]= n/2,共 n/2 大类拆分。在每一类拆分时,a[1]= i ,a[2]= n - i ,从 k=2,继续拆分从 a[k]开始,a[k]能否再拆分取决于 a[k]/2 是否大于等于 a[k-1]。递归过程的参数 t  指向要拆分的数 a[k],于是有算法:

#include<iostream>
using namespace std;
int a[100];
static int sum=0;
void Split(int t)
{
 int i;
 for(i = 1; i <= t; i++)
 {
  cout<< a[i]<<"\t";
 }
 ++sum;
 cout<< endl;


 int j = t;//j是t的副本,改变j,不影响t,每一轮的a[t]值是不变的
 int L = a[j];
  for(i = a[j-1]; i <= L/2; i++)//a[t]处取值的范围为a[t-1]~a[t]/2
 {
  a[j] = i; 
  a[j+1] = L - i; 
  Split(j+1);
 }
 /* for(i = a[t-1]; i <=a[t]/2; i++)//此种写法不正确
  { a[t] = i; 
  a[t+1] = a[t] - i; 
  Split(t+1);
  }*/
}


void SplitNum(int n)
{
 int i;
 for(i = 1; i <= n/2; i++)
 {
  a[1] = i;
  a[2] = n - i;
  Split(2);
 }
}
void main()
{
 const int n=7;
SplitNum(n);
cout<<"总数"<<sum<<endl;


}

运行结果:

    

原链接:http://jimobit.blog.163.com/blog/static/2832577820071021112229350/

方法二:回溯法

 针对所给问题,定义问题的解空间;

如本题对5的拆分来说,1<=拆分的数<=5。 确定易于搜索的解空间结构;

如本题对5的拆分来说,用x[]数组来存储解,每个数组元素的取值范围都是1<=拆分的数<=5,

从1开始搜索直到5。 搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

如本题对5的拆分来说,为了避免重复,x>=x[j](i>j),如x[]={2,3}满足条件而x[]={3,2}就不满足条件不是可行解即无效。 

#include<stdio.h> 
#include<stdlib.h> 
//功能:回溯法进行自然数的拆分 
 void splitN(int n,int m);// n是需要拆分的数,m是拆分的进度。  
 int x[1024]={0},total=0 ;// total用于计数拆分的方法数,x[]用于存储解 
void main()
{     
int n  ;    
printf("please input the natural number n:");     
scanf("%d",&n);    
splitN(n,1);      
 printf("There are %d ways to split natural number %d. ",total,n); 
}   
 void splitN(int n,int m)  
 {//n是需要拆分的数,m是拆分的进度     
int rest,i,j;         
for(i=1;i<=n;i++)      
{//从1开始尝试拆分                 
if(i>=x[m-1])         
{//拆分的数大于或等于前一个从而保证不重复             
x[m]=i; // 将这个数计入结果中                       
   rest=n-i ;// 剩下的数是n-i,如果已经没有剩下的了,并且进度(总的拆分个数)大于1,说明已经得到一个结果了             
if(rest==0&&m>1)             
{                  
total++;                 
printf("%d\t",total);                 
for(j=1;j<m;j++)                 
{                      
printf("%d+",x[j]);                 
}                  
printf("%d ",x[m]);         
printf("\n");              
}             
else             
{                  
splitN(rest,m+1);// 否则将剩下的数进行进度为m+1拆分             
}              
x[m]=0;// 取消本次结果,进行下一次拆分。环境恢复,即回溯 
}
}
 }

运行结果:

      

方法三:非递归

#include "stdio.h"
//功能:非递归实现自然数的拆分
void  Divinteger(int n)
{
 if (n==1) //处理输入值为1的情况,因为如果输入1,就不用再划分
 { 
  printf("只有一种划分:1\n");
  return;
 }
 if (n == 2) //处理输入值为2 的情况,如果输入为2,只要写成1+1就可以了。
 {
 printf("第1种划分:2\n");
 printf("第2种划分:1 1\n");
 printf("一共有2种划分!\n");
 return;
 }
 int *a =new int(n); //定义动态数组,因为当n大于2时,如3=2+1,这时就需要对2进行相似划分,其大小是不固定的,因而用动态数组。
 int div=0; //每一行划分的数组下标
 int k=1;//记录种类的值。
 a[0] = n - 1;
 a[1] = 1;
 div = 2; //第二次一定划分为2个值。n-1和1
 printf("第%2d种划分:%d \n",1,n);
 int i;
 do{
  k++;
  printf("第%2d种划分:",k);
  printf("%d", a[0]);
  for (i = 1; i < div; i++)
   printf("  %d", a[i]);
  printf("\n");


 int s = 0;
 
 do{
  s += a[--div];
 }while (div >= 0 && a[div] == 1); //这里用来找到非1的可以继续划分的值,如5 1,通过这一部找到5;
 if (div == -1)
  break; 
 int d = a[div] - 1; //判断是否还有数字可以继续划分。
 if (d == 1) //这句户用来为下一个循环做准备,
 {
  while (s > 0) //s存放着下一行一共有几个值
  {
  a[div++] = 1; //将下一行的值全部赋值为1.等待下一个循环输出。
  s--;
  }
 }
 else //表示还有值可以继续划分
 {
  do{
   a[div++] = d;// 相当于向右移动一位,进入下一次划分(对4)
   s -= d;
  }while (s >= d); //得出a[0]=4 
  if (s != 0)
  a[div++] = s; //得出a[1]=2    div=2
 }
 
 }while (1);
 
 printf("\n一共有 %d 种方法!\n\n",k);
}
 
void main()
{
 int n;
 printf("输入你要求的划分整数值:\n");//输入n值
 scanf("%d", &n);
 Divinteger(n); //调用划分函数
}

  

原链接:http://blog.sina.com.cn/s/blog_5052ef9d0100dcf8.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值