/*输入两个正整数,求这两个数的所有公因数之和
在一般的程序中,都采用循环,两个数字小还可以,但是当两个数非常大,假如都是4294967291时将耗费很长时间,本代码采用辗转相除法和欧拉法判断质数,可以轻松的在0.5S内求出2-4294967295的任意两个数的公因数之和:
代码如下:*/
# include <stdio.h>
# include <stdlib.h>
# include <math.h>
typedef unsigned long Elem;
Elem zhishu(Elem a);
Elem gys(Elem m,Elem n);
void main(){
Elem a[]={0,0,0,0},i;
char aa[][3]={"一","二"};
long double sum=0;
do{
for(i=0;i<2;i++) //输入两个数存在a[0],a[1]中
while(a[i]<2||a[i]>4294967295) //1排除在外
{
printf("输入第%s个数(2--4294967295):",aa[i]);
scanf("%lu",&a[i]);
while(getchar()!='\n');
if(a[i]<2||a[i]>4294967295)
printf("输入有误,请重新");
}
printf("%lu与%lu的公因数之和\n",a[0],a[1]);
a[3]=a[2]=gys(a[0],a[1]); //a[2]=a[3]存有a[0],a[1]的最大公因数
for(i=1;i<a[3];i++)//a[2]不动,对a[3]进行更改,从1开始;
if(a[2]%i==0)//当a[2]整除i时
{
a[3]=a[2]/i; //循环主要在数大的时候浪费时间,因为数大的地方公因数密度少,用a[3]存储数大的公因数
sum+=i;
if(i<a[3])
{
sum+=a[3];
printf("%lu %lu ",i,a[3]);
if(zhishu(a[3])) //此处主要在数字大的地方节省时间,比如4294967291,没有这句话,要花费很长时间
break;
}
else if(i==a[3])
printf("%lu ",i);
}
printf("\n=%.0lf",sum+(a[2]-1?0:1)); //当用户输入4200000000时,如果不用double 存储,则超限
do{
printf("\n1:重新做\n2:退出\n3:清屏+重做:\n");
scanf("%lu",&i);
while(getchar()!='\n');
}while(i<1||i>3);
sum=a[0]=a[1]=a[2]=0; //对a[3]保留因为循环的第一句话只适合程序运行开始时提示一下
if(i==3)
system("cls");
}while(i-2);
}
Elem zhishu(Elem a) //欧拉判断质数
{
Elem i,b;
b=sqrt(a);
for(i=2;i<=b;i++)
if(a%i==0) return 0; //为合数时返回0
return a; //否则返回本身,包含a=1的情形,即把1看成质数
}
Elem gys(Elem m,Elem n)//辗转相除法求mn的最大公因数,m&&n!=0
{
Elem A,B;
if(n>m)
{
A=m;m=n;n=A; //保证m>n
}
while((B=m%n)!=0)
{
m=n;n=B;
}
return n;
}
在visualc++6.0中unsigned __int64无法以形参的方式传入sqrt()函数中而__int64 可以,所以在用到大数的时候,可以稍微改一些地方而不用重写sqrt()函数,
如果我要输入a=18446744073709551615,那么要计算它的平方根可以将其除以4或其他数,用__int64 b=a/2;b=sqrt(2)*sqrt(b);这样就可以得到大概的值,如要求
16446744073709551615是否为质数,那么可以用以下代码:
当然代码可以优化,不过这样看的更直观一些.
这样就可以求2-18446744073709551615所有的质数;
typedef unsigned __int64 Elem;
Elem zhishu(Elem a) //欧拉法判断质数
{
Elem i;
long double c=0;
__int64 b=0;
if(a>=N)
{
b=a/2;
b=sqrt(2)*sqrt(b);
}
else
{
b=a;
b=sqrt(b);
}
for(i=2;i<=b;i++)
if(a%i==0) return 0; //为合数时返回0
return a; //否则返回本身,包含a=1的情形,即把1看成质数
}
由此可以求2-18446744073709551615中的--任意两个数的所有公因数之和:
代码如下:
# include <stdio.h>
# include <stdlib.h>
# include <math.h>
typedef unsigned __int64 Elem;
const __int64 N=9223372036854775807;//__int64的最大值
const unsigned __int64 UN=18446744073709551615;
const unsigned __int64 INDEX=1000000000000000000;//每十八位进一位
Elem sum[3]={0,0,0};
Elem zhishu(Elem a);
Elem gys(Elem m,Elem n);
void main(){
Elem a[]={0,0,0,0},i;
int j=0,k=0;
char aa[][3]={"一","二"};
do{
for(i=0;i<2;i++) //输入两个数存在a[0],a[1]中
while(a[i]<2) //1排除在外
{
printf("输入第%s个数(2--%I64u):\n",aa[i],UN);
scanf("%I64u",&a[i]);
while(getchar()!='\n');
if(a[i]<2)
printf("输入有误,请重新");
}
printf("%I64u与%I64u的公因数之和\n",a[0],a[1]);
a[3]=a[2]=gys(a[0],a[1]); //a[2]=a[3]存有a[0],a[1]的最大公因数
for(i=1;i<a[3];i++)//a[2]不动,对a[3]进行更改,从1开始;
if(a[2]%i==0)//当a[2]整除i时
{
a[3]=a[2]/i; //循环主要在数大的时候浪费时间,因为数大的地方公因数少,用a[3]存储数大的公因数
sum[0]+=i;
while(sum[j]>=INDEX)
{
sum[j+1]+=sum[j]/INDEX;
sum[j++]%=INDEX;
k=j;
}
j=0;
if(i<a[3])
{
sum[0]+=a[3];
while(sum[j]>=INDEX)
{
sum[j+1]+=sum[j]/INDEX;
sum[j++]%=INDEX;
k=j;
}
j=0;
printf("%I64u %I64u ",i,a[3]);
if(zhishu(a[3]))
break;
}
else if(i==a[3])
printf("%I64d ",i);
}
sum[j]+=(a[2]-1?0:1);
printf("\n=%I64u",sum[k--]);
if(k>=0)
printf("%018I64u",sum[k]);
do{
printf("\n1:重新做\n2:退出\n3:清屏+重做:\n");
scanf("%I64u",&i);
while(getchar()!='\n');
}while(i<1||i>3);
sum[0]=sum[1]=sum[2]=0;
a[0]=a[1]=a[2]=j=k=0;
if(i==3)
system("cls");
}while(i-2);
}
Elem zhishu(Elem a) //欧拉判断质数
{
Elem i;
long double c=0;
__int64 b=0;
if(a>=N)
{
b=a/2;
b=sqrt(2)*sqrt(b);
}
else
{
b=a;
b=sqrt(b);
}
for(i=2;i<=b;i++)
if(a%i==0) return 0; //为合数时返回0
return a; //否则返回本身,包含a=1的情形,即把1看成质数
}
Elem gys(Elem m,Elem n)//辗转相除法求mn的最大公因数,m&&n!=0
{
Elem A,B;
if(n>m)
{
A=m;m=n;n=A; //保证m>n
}
while((B=m%n)!=0)
{
m=n;n=B;
}
return n;
}
/*虽然当数非常大时这个求解时间也很长,但是也算是很有效率了*/