最多约数问题

问题描述: 
正整数x的约数是能整除x的正整数。正整数x 的约数个数记为div(x)。例如,1,2,5,10 都是正整数10 的约数,且div(10)=4。设a 和b 是2 个正整数,a≤b,找出a和b之间约数个数最多的数x。 
问题简化: 
对于给定的2个正整数a≤b,编程计算a 和 b 之间约数个数最多的数。

来源:《计算机算法设计与分析(第三版)》作者:王晓东,出版社:电子工业出版社


算法一:

思想:算出每一个数的公约数个数,在此范围内一次比较,每出现一个暂时最大的值就把其赋给一个值

#include<iostream> 
#include<math.h>
using namespace std;

int getDiv(int n)                  //返回一个数的公约数的个数 
{   
    int sum = 0;
    
    if(n<=0)    return 0; 
    if(n==1)    return 1;

    for(int i=1;i<=n/2;i++)       //公约数只在0-n/2范围内 
    { 
        if (n % i == 0)
        {
        sum++;
        }   
    } 
    return (sum+1); 
}

int main() 
{ 
    int a,b; 
    int maxdiv =1; 
    int number=0;
     
    cin>>a>>b;

    for (int i = a; i <= b; i++) 
    { 
        if ( maxdiv < getDiv(i) ) 
        { 
            maxdiv = getDiv(i);
            number=i; 
        } 
    } 
    cout<<"有最多公约数的是:"<<number<<endl<<"公约数个数是:"<<maxdiv<<endl; 
    system("pause"); 
    return 0; 
}

算法二:

原书给出的参考答案是这样的:

思想:设正整数x的质因子分解为
x=p1^N1 × p2^N2 ×……pi^Ni
则 div(x)=(N1+1)(N2+1)……(Ni+1)

下面代码我表示至今没看懂(蛋疼):

#include<iostream>
using namespace std;
#define max Max
const long MAXP = 100000;
long prim[MAXP];
long max, numb, PCOUNT; //max存放最多约数个数,numb存放约数个数最多的数
void primes(); //用筛选法产生质数存于prim数组中
void search(long from, long tot, long num, long low, long up);
int main()
{
    primes();
    long l, u;
    cin >> l >> u;
    if ((l == 1) && (u == 1))
    {
         max = 1;
          numb = 1;
    }
    else
    {
        max = 2;
        numb = l;
         search(1, 1, 1, l, u);
    }
     cout << max << endl << numb << endl;
     system("pause");
     return 0;
}

void primes() 
{
    bool get[MAXP+1];
    long i;
    for (i = 2; i <= MAXP; i++)
          get[i] = true;
     for (i = 2; i <= MAXP; i++)
         if (get[i])
         {
              long j = i + i;
              while (j <= MAXP) 
             {
                  get[j] = false;
                  j += i;
              }
         }
    long ii, j;
    for (ii = 2, j = 0; ii <= MAXP; ii++)
        if (get[ii]) prim[++j] = ii;
    PCOUNT = j;
}

// 区间[low,up]上,tot为当前约数最多个数,num为约数个数最多的数,
//from表示现在是第几个质数。

void search(long from, long tot, long num, long low, long up)
{
if (num >= 1)
        if ( (tot > max) || ((tot == max) && (num < numb)) )
        {
              max = tot;
              numb = num;
         }
   if ((low == up) && (low > num)) search(from, tot*2, num*low, 1, 1);
   for (long i = from; i <=PCOUNT; i++)
   {
         if (prim[i] > up) return; 
         else
         {
                long j = prim[i], x = low - 1, y = up, n = num, t = tot, m = 1;
                while (true)
               {
                       m++;
                        t += tot;
                         x /= j;
                        y /= j;
                        if (x == y) break;
                        n *= j;
                       search(i+1, t, n, x+1, y);
               }
              m = 1 << m;
              if (tot < max / m) return;
        }
   }
}

总结:对于很大的数应该采用方法二才能在最短时间内解决问题。但算法二设计很强的数学基础,难于理解。

算法三:

思想:设正整数x的质因子分解为
x=p1^N1 × p2^N2 ×……pi^Ni
则 div(x)=(N1+1)(N2+1)……(Ni+1)

关键思想:任何合数都可以分解成几个质因数乘积的形式。

#include <stdio.h>

#define MAXP 1000

int Count;       //Count是prime数组的下标
int isPrime[MAXP],prime[MAXP];  //isPrime数组是判断一个数是不是质数,prime存放质数

void CalPrime();
int GetPrimeNum(int n);

int main()
{
	int a,b,maxNum,maxCount,i,len;
	CalPrime();

	while (maxNum=1,maxCount=0,scanf("%d%d",&a,&b)!=EOF)
	{
		for (i=a;i<=b;i++)
		{
			len=GetPrimeNum(i);
			if (len>maxCount)
			{
				maxCount=len;
				maxNum=i;
			}
		}
		printf("数 %d 有 %d 个约数\n\n",maxNum,maxCount);
	}
	
	
	return 0;
}

void CalPrime()
{
	int i,j;

	for (i=2;i<=MAXP;i++)
	{
		if (i%2==0)     //偶数直接判断不是质数(2另外处理)
		{
			isPrime[i]=0;
		}
		else            //奇数先假设是质数
		{
			isPrime[i]=1;
		}
	}

	isPrime[2]=1;
	for (i=2;i<=MAXP/2;i++)     
	{
		if (isPrime[i]==1)       //如果一个数是奇数,那么它的倍数不是质数
		{
			for (j=i+i;j<=MAXP;j+=i)
			{
				isPrime[j]=0;
			}
		}
	}

	Count=0;
	for (i=2;i<=MAXP;i++)    //质数存进prime数组
	{
		if (isPrime[i]==1)
		{
			prime[Count++]=i;
		}
	}

}

//获取数n的约数个数
//利用公式(每个合数可以拆分)
//正整数x的质因子分解为
//x=p1^N1 × p2^N2 ×……pi^Ni  (pi为质数,Ni为指数)
//则 div(x)=(N1+1)(N2+1)……(Ni+1)

int GetPrimeNum(int n)  
{
	int temp=n,t=0,count=1,index=0; //temp为数n的副本,t记录某个质数的指数,count为约数个数,index为质数下标
	if (n==1)
	{
		return 1;
	}
	if (isPrime[n]==1)   //如果是质数,返回2
	{
		return 2;
	}
	while (prime[index]<=temp)  //核心
	{
		if (n%prime[index]==0)
		{
			t++;
			n/=prime[index];
		}
		else if (t==0)
		{
			index++;
		}
		else
		{
			count*=(t+1);
			t=0;
			index++;
		}
	}
	return count;
}

总结:此程序相对穷举法程序实例1来说,以指数级提高效率,但最快的还是程序2,是对前两个程序的折中。但此程序增加了两个存放素数的数组,以不同方式存放素数,很有用。并且本程序中的构造素数数组的方法值得借鉴。

本文参考:http://apps.hi.baidu.com/share/detail/19469948


你可能感兴趣的:(最多约数问题)