您的位置:首页 > 其它

HDU 1108 最小公倍数【欧几里得算法】【更相减损术】【stein算法】

2015-07-10 15:07 453 查看


最小公倍数

Problem Description

给定两个正整数,计算这两个数的最小公倍数。

Input

输入包含多组测试数据,每组只有一行,包括两个不大于1000的正整数.

Output

对于每个测试用例,给出这两个数的最小公倍数,每个实例输出一行。

Sample Input

10 14


Sample Output

70


注意 :
1:最小公倍数 = a*b/最大公约数 注意超过范围32位

用a*(b/最大公约数)就不会超过32位了
2:最大公约数GCD a%b 用较大的a%较小的b 若a%b!=0 ,再反复求余 一直到a%b==0 为止 b 就是最大公约数
3:欧几里得算法(非递归方式)
int gcd(int da,int xiao)
{ int temp;
while (xiao!=0) {
temp=da%xiao;
da=xiao;
xiao=temp;
}
return(da);
}

#include<stdio.h>
int GCD (int a,int b)//最大公约数
{
int t;
if(a<b){
t=a;
a=b;
b=t;
}
if(a%b==0){
return b;
}
else{
return GCD(b,a%b);//欧几里得递归形式 也可表示为return a%b?GCD(b,a%b):b;
}
}
int LCM (int a,int b)//最小公倍数
{
return a/GCD(a,b)*b;
}
int main (void)
{
int a,b;
while(~scanf("%d%d",&a,&b)){
printf("%d\n",LCM(a,b));
}
return 0;
}

1、辗转相除法又称欧几里得算法(求最大公约数)
思路:
第一步 两个整数的最大公约数等于其中较小的数和两数的相除余数的最大公约数
第二步 利用最大公约数求最小公倍数 设两个数是a,b最大公约数是p,最小公倍数是q, 那么有这样的关系:ab=pq 所以q=ab/p
#include <stdio.h>
main()
{
unsigned int i,j,n,temp,a;
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&i,&j);
//两个数的积 作为后面求最小公倍数用
a=i*j;
//找到最大数 j
if (i>j)
{
temp=i;
i=j;
j=temp;
}
//辗转相除法
while(temp=j%i)
{
j=i;
i=temp;
}
printf("%d %d\n",i,a/i);
}
}

复杂度: 辗转相除法的运算速度为 O(n),其中 n 为输入数值的位数。
缺陷: 欧几里德算法是计算两个数最大公约数的传统算法,无论从理论还是从实际效率上都是很好的。但是却有一个致命的缺陷,这个缺陷在素数比较小的时候一般是感觉不到的,只有在大素数时才会显现出来。

一般实际应用中的整数很少会超过64位(当然现在已经允许128位了),对于这样的整数,计算两个数之间的模是很简单的。对于字长为32位的平台,计算两个不超过32位的整数的模,只需要一个指令周期,而计算64位以下的整数模,也不过几个周期而已。但是对于更大的素数,这样的计算过程就不得不由用户来设计,为了计算两个超过64位的整数的模,用户也许不得不采用类似于多位数除法手算过程中的试商法,这个过程不但复杂,而且消耗了很多CPU时间。对于现代密码算法,要求计算128位以上的素数的情况比比皆是,设计这样的程序迫切希望能够抛弃除法和取模。

2、更相减损术
思路:

任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。

#include <stdio.h>
main()
{
int i,j,m,c,proc,gcd;
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&i,&j);
c=0;
proc=i*j;
//执行第一步 同为偶数 就除2
while (((i & 0x1)==0)&&((j & 0x1)==0)){
i=i>>1;
j=j>>1;
c++;
}
//执行第二步 减数和差不相等则循环相减
while(i!=j){
if (i>j) i-=j;
else j-=i;
}
gcd=i<<c;
printf("%d %d\n",gcd,proc/gcd);
}
}

3、Stein算法
Stein算法有点类似于更相减损法,它是针对欧几里德算法在对大整数进行运算时,需要试商导致增加运算时间的缺陷而提出的改进算法。
思路:

如果An=0,Bn是最大公约数,算法结束
如果Bn=0,An是最大公约数,算法结束
如果An和Bn都是偶数,则An+1=An/2,Bn+1=Bn/2,Cn+1=Cn*2
如果An是偶数,Bn不是偶数,则An+1=An/2,Bn+1=Bn,Cn+1=Cn
如果Bn是偶数,An不是偶数,则Bn+1=Bn/2,An+1=An,Cn+1=Cn
如果An和Bn都不是偶数,则An+1=|An-Bn|/2,Bn+1=min(An,Bn),Cn+1=Cn

int gcd(int a, int b)
{
//找出最小的数b
if (a<b)
{
int temp = a;
a = b;
b = temp;
}
//b为0 则a为公约数
if (b==0) return a;
//同为偶数 则求a/2 和 b/2的公约数
if ((a&0x1)==0 && (b&0x1)==0)
return 2*gcd(a>>1, b>>1);
//a偶 b奇 则求a/2 和b的公约数
if ((a&0x1)==0 && (b&0x1)!=0)
return gcd(a>>1, b);
//a奇 b偶 则求a 和b/2的公约数
if ((a&0x1)!=0 && (b&0x1)==0)
return gcd(a, b>>1);
//a、b都不为偶数 则求(a-b)/2 和 b 的公约数 两个奇数相减为偶数
if ((a&0x1)!=0 && (b&0x1)!=0)
return gcd((a-b)>>1, b);
}

main()
{
int i,j,m,proc,a;
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&i,&j);
proc=i*j;
a=gcd(i,j);
printf("%d %d\n",a,proc/a);
}
}

优化后

#include <stdio.h>
int gcdcore(int a,int b) {
if (a==0) return b;
if (b==0) return a;
while ((a & 0x1)==0) {
a=a>>1;
}
if (a<b) {
b=(b-a)>>1;
return gcdcore(b,a);
}
else {
a=(a-b)>>1;
return gcdcore(a,b);
}
}
int gcd(int a,int b) {
int c=0;
while (((a & 0x1)==0)&&(( b & 0x1 )==0)) {
a=a>>1;
b=b>>1;
c++;
}
if ((a & 0x1) == 0) {
a=a>>1;
return gcdcore(a,b)<<c;
}
else {
return gcdcore(b,a)<<c;
}
}
//通过改善流程省去了低效的a,b交换,同时用while循环代替了过多的递归,以节约空间和时间。
//迭代过程化为子过程以减少不必要的判断
//子过程中只有减法运算后的值可能为偶数所以只需要对a判断是否为偶数
main()
{
int i,j,m,proc,a;
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&i,&j);
proc=i*j;
a=gcd(i,j);
printf("%d %d\n",a,proc/a);
}
}
优化过的函数先进行预处理,通过一个while循环约掉所有2,递归部分交给gcdcore函数完成,减少了递归次数,从而提高了效率。

优点: Stein算法只有整数的移位和加减法 更适应程序。
复杂度: 对于大素数,Stein算法将更有优势。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: