题目:我们知道1+2=3;4+5=9;2+3+4=9;写一个程序,对于64位正整数num,输出它所有可能的连续自然数。
我首先想到的是全局搜索方法,但是时间复杂度比较大。程序要执行很长时间才能出结果。
方法二通过寻找划分的数学规律,从数学表达式的基础上直接得到结果。主要有以下几个要点。
1.当划分个数div确定时,输出的表达式是唯一的,即first_value唯一,故可以只需要每个div循环执行一次。
2.div_max的范围确定。num=first_value+(first_value+1)+......(first_value+div-1)>=1+2+3+.....(div-1)=div*(div-1)/2 当first_value=0时等号成立
则2*num>div*(div-1)>(div-1)*(div-1) ---->>div<1+sqrt(2*num)
3. div=3---->num=(int_n-1)+int_n+(int_n+1)=3*int_n
div=4---->num=int_n-1+int_n+int_n+1+int_n+2=4*int_n+2
依次类推:
当div%2=0时,num=int_n*div ----->int_n=num*1.0/div 表达式右边进行浮点运算 int_n==float_n且连续相加的第一个数大于零则存在int_n
当div%2!=0时,num=int_n*div+div/2 ------>int_n=(num-div/2.0)/div 表达式右边进行浮点运算 int_n==float_n且连续相加的第一个数大于零则存在int_n
程序如下:
#include<stdio.h>
#include<math.h>
void main()
{
int num,int_n,i,n_min,n_max;//num=n_min+......+n_max;
float float_n=0;
float first_value=0; //num=first_value+(first_value+1)+......+(first_value+div-1)
int div=3,sum=0,div_max; //div<div_max
scanf("%d",&num);
div_max=1+sqrt(2*num);
printf("=====================方法一======================\n");
//方法一采用全局搜索,对于给定num 依次将其划分为div个连续数相加,搜索是否存在first_value使
//num==div个连续数相加,若存在肯定唯一,否则不存在。
for(div=3;div<div_max;div++)
{
for(first_value=1;first_value<=(num/div);first_value++)
{
sum=0;
for( i=0;i<div;i++)
sum=sum+first_value+i;
if(num==sum)
{
printf("div=%d\tfirst_value=%f\n",div,first_value);
}
}
}
printf("1 end of div!!!\n");
//方法二寻找将num划分为div个连续数相加的数学规律
printf("=====================方法二======================\n");
for(div=3;div<div_max;div++)
if(div%2==0)
{
float_n=(num-div/2.0)/div;
int_n=(num-div/2)/div;
n_min=int_n+1-div/2;
n_max=int_n+div/2;
if((float_n==int_n)&&(n_min>0))
{ printf("div= %d , num=%d=",div,num);
for(i=n_min;i<n_max;i++)
printf("%d+",i);
printf("%d\n",n_max);
}
}
else
{
int_n=(float)num/(float)div;
float_n=(float)num/(float)div;
n_min=int_n-div/2;
n_max=int_n+div/2;
if((float_n==int_n)&&(n_min>0))
{ printf("div= %d , num=%d=",div,num);
for(i=n_min;i<n_max;i++)
printf("%d+",i);
printf("%d\n",n_max);
}
}
printf("2 end of div!!!\n");
运行如下: