最大约数个数算法分析

  实验问题描述

正整数x的约数是能整除x的正整数。正整数x的约数个数记为div(x),例如,1,2,5,10都是正整数10的约数,且div(10)=4.ab2个正整数,a<=b,找出ab当中约数个数最多的数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;//返回计算的个数

   //其实有可能出现误差,如果这个含有最大约数的数还有一个很大的素数因子没有检测出来

   //又或者有一个数由于其素数公因子太多,因此少计算进去,可能存在误差

   //但是从另一方面考虑,如果这个数真的含有最多的约数,那么它的素数公因子必须很小

   //因此,出错的概率必须和ab之间存在的个数有关

}

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]++;

                            //记住ab的下标是对应的

                               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;

    }

   }