问题描述:
正整数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