最大约数个数算法分析
实验问题描述
正整数x的约数是能整除x的正整数。正整数x的约数个数记为div(x),例如,1,2,5,10都是正整数10的约数,且div(10)=4.设a和b是2个正整数,a<=b,找出a和b当中约数个数最多的数x,输出其约数个数值。
实验目的
本次实验通过利用数论知识,建立素数表,弥补累除法在时间复杂度上的缺陷,同时又结合累除法在小范围内能够计算出最准确的约数个数的特性,
在时间复杂度上获得最大的优化。
实验知识准备过程
累除法的基本思想:计算正整数a的约数个数时,先设置一个统计数变量count,统计a的约数个数,从1开始计算是否能整除a,如果能整除,count
加1,否则1自增,依次累除,直到累除到自身,退出循环。
求解每一个正整数约数个数流程图如下:
累除法的缺点:当两个数的范围在10万到10亿之间,统计所有的数的约数个数所使用的时间是非常庞大的。(经过测试表明:在10万以上的正整数用累除法需要超过1s的计算时间)。
改进方法:根据数论的知识,每一个正整数都可以分解成若干个素数的乘积,例如:12=2*2*3,计算其约数个数的方法如下:a(12)=(1+1)*(1+1)*(1+1)=6,意思表达为:素数指数加一后的乘积。因此,避免用累除法得出约数个数,将会缩短计算时间。利用数论的知识,需要构建一张素数表以存储素数,然后用正整数整除这张素数表,得出其到底有多少个素数,然后存储在一个数组里面。
实现的流程图如下:
素数表的缺点:当两个数的范围较小,但是两个数的数值很大的时候,会出现很大的误差,原因如下:当一个数含有的约数超过素数表的范围,就会出现错误。因此必须结合累除法的优点,减少误差。
实验的具体过程
具体的实现过程如下:
判断输入的两个正整数的范围:当两者的范围大于100时,表示两者之间需要测试的数据比较少,使用累除法,能够减少误差;当两者的范围超过100时,选择素数表进行计算。
流程图如下:
时间复杂度分析
时间复杂度分析基于以下假设:两个正整数分别是a,b(a<=b),假设每计算一次除法所花的时间是1,时间复杂度设为0(N)
累除法:时间复杂度0(N)=(b-a)(b+a)/2
素数法:时间复杂度0(N)=(b-a)*1000
从上述两条表达式可以看出:当a>1000时,利用素数法,能够缩短计算的时间,尤其是当a,b的数值在万以上时,计算时间更加明显。
六.核心代码如下:
调用的函数核心代码如下:
1)创建素数表函数numlist()
int numlist(int a[])//建立素数表
{
int flag=0;//确定数组当前存储素数的位置
bool repeat=true;//循环标志位设定
int j=0;
for(int i=2;i<1000;i++)//将通过设定i的上限来扩大搜索的范围
{
while(repeat)
{
if((j==flag)&&(i%a[flag]!=0))//判断是否已经到了当前素数表的最后一个元素,还不能整除的,把当前数字推入素数表
{
repeat=false;
flag++;
a[flag]=i;
}
else
{
if(i%a[j]==0)//如果能整除跳出while语句
{
repeat=false;
}
else//否则继续判断
{
j++;
}
}
}
repeat=true;//i每进行一次测试前,必须置值为真
j=0;//下一个数的除数应该从a[0]开始,所以j应该置零
}
return flag;//返回计算的个数
//其实有可能出现误差,如果这个含有最大约数的数还有一个很大的素数因子没有检测出来
//又或者有一个数由于其素数公因子太多,因此少计算进去,可能存在误差
//但是从另一方面考虑,如果这个数真的含有最多的约数,那么它的素数公因子必须很小
//因此,出错的概率必须和a和b之间存在的个数有关
}
2).求约数个数函数
int test(int a[],int start,int finish,int flag)
{
bool repeat=true;
int testnumber=start;//存储含有最大约数的数,以备检查(利用累除法检查)
int numberflag=0;//设置比较位
int statist=1;//到底应该设置统计值为多少需要分析
int b[10000]={0};//初始化当前标志位记载有多少个这样相同的素数,并且初始化
int j=0;//保存指向标志位数组的当前光标
int temp=0;//循环除法的载体
for(int i=start;i<=finish;i++)
{
//j不能否超越flag的存在
for( j=0;i>=a[j];j++)//不能超过素数表的界限,但是必须排除这个数小于10000,当这个数很小时,没有必要比较下去
{
if(j>flag)
{
break;
}
else
{
temp=i;
while(temp%a[j]==0)//如果能够整除,标志位加1,同时记得除去已经添加的次数
{
b[j]++;
//记住a和b的下标是对应的
temp=temp/a[j];
}
}
}
//现在统计这个数含有的公因子个数
for(int k=0;k<=j;k++)
{
if(b[k]!=0)
{
statist=statist*(b[k]+1);
}
}
//cout<<i<<"的约数个数:"<<statist<<endl;
if(numberflag<statist)//取其中最大的数字
{
numberflag=statist;
testnumber=i;
}
statist=1;//重置
for( k=0;k<=j;k++)
{
b[k]=0;
}
}
转载于:https://blog.51cto.com/fengyuzaitu/1580960