编程之美: 第二章 数字之魅 2.7最大公约数问题

/*
最大公约数问题:
写一个程序,求两个正整数的最大公约数(Greatest Common Divisor,GCD)。如果两个正整数都很大,有什么简单的算法


解法2:
用到了取模运算,称为算法瓶颈。如果一个数能够同时整除x和y,则比能同时整除x-y和y;而能够同时整除x-y和y的数也必定能够同时整除x和y
f(x,y) = f(x-y,y),在实际操作中,如果x<y,可以先交换,边求一个整数和一个负数的最大公约数的出现。

解法3:
分析公约数的特点:对于y和x来说,如果y = k*y1,x = k*x1。那么有f(y,x) = k*f(y1,x1).
如果x = p*x1,假设p是素数,并且y%p != 0(即y不能被p整除),那么f(x,y) = f(p*x1,y) = f(x1,y)

2是一个素数,同时对于二进制表示的大整数而言,可以很容易除以2和乘以2的运算转换成移位运算,避免触发。
取p = 2
若x,y均为偶数,f(x,y) = 2*f(x/2,y/2) = 2*f(x>>1,y>>1)
若x为偶数,y为奇数,f(x,y) = f(2*x/2,y) = f(x/2,y) = f(x>>1,y)
若x为奇数,y为偶数,f(x,y) = f(x,y>>1)
若x,y均为奇数,f(x,y) = f(y,x-y)(没想到用减法来构成偶数,这样最终x与y中至少有一个是偶数)
最欢的时间复杂度是O(log2(max(x,y)))

输入:
1 100 100 210 001
120 200 021

8
12
输出:

4
*/

/*
关键:
1 f(x,y)表示x与y的最大公约数,取k = x/y,b = x%y,那么x = k*y + b。如果一个数能够同时整除x和y,必能同时整除b和y;而如果能够同时整除b和y的数也必定
能够同时整除x和y;x和y的公约数与y和x%y的公约数是相同的、
f(x,y) = f(y,x%y)(x>=y>0)
2 return gcd_minus(a-b,b);//利用gcd(a,b)  = gcd(a-b,b)来做,不使用取模运算,提高速度,缺点:迭代次数可能增加,
3 	if(y == 0)//如果较小数已经变成0,那么直接返回x
	{
		return x;
	}
	else if((x & 1) == 0)//如果x为偶数,x & 1 == 0
	{
		if((y & 1) == 0)//如果y为偶数
		{
			return (gcd_move(x >> 1,y >> 1) << 1);//注意这里要将双方的偶数提取出来
*/

#include <stdio.h>

static int _iCnt1 = 0;
long long gcd(long long a,long long b)
{

	_iCnt1++;
	return !b ? a : gcd(b,a%b);
}

static int _iCnt2 = 0;
long long gcd_minus(long long a,long long b)
{

	_iCnt2++;
	if(a < b)//确保gcd()中的两个参数必须是第一个>=第二个,否则不能做
	{
		return gcd_minus(b,a);
	}
	if(b == 0)//递归出口
	{
		return a;
	}
	else
	{
		return gcd_minus(a-b,b);//利用gcd(a,b)  = gcd(a-b,b)来做,不使用取模运算,提高速度,缺点:迭代次数可能增加,
	}
}

static int _iCnt3 = 0;
long long gcd_move(long long x,long long y)//移位的gcd
{
	
	_iCnt3++;
	if(x < y)
	{
		return gcd_move(y,x);
	}
	if(y == 0)//如果较小数已经变成0,那么直接返回x
	{
		return x;
	}
	else if((x & 1) == 0)//如果x为偶数,x & 1 == 0
	{
		if((y & 1) == 0)//如果y为偶数
		{
			return (gcd_move(x >> 1,y >> 1) << 1);//注意这里要将双方的偶数提取出来
		}
		else
		{
			return gcd_move(x >> 1,y);
		}
	}
	else
	{
		if((y & 1) == 0)//如果y为偶数
		{
			return gcd_move(x,y >> 1);
		}
		else
		{
			return gcd_move(y,x-y);
		}		
	}
}

void swap(long long * pNum1,long long *pNum2)
{
	long long lTemp = *pNum1;
	*pNum1 = *pNum2;
	*pNum2 = lTemp;
}

void process()
{
	long long n,m;
	while(EOF != scanf("%lld %lld",&n,&m))
	{
		if(n < m)
		{
			swap(&n,&m);
		}
		printf("%lld\n",gcd(n,m));
		printf("%lld\n",gcd_move(n,m));
		printf("%d %d\n",_iCnt1,_iCnt3);
		printf("%lld\n",gcd_minus(n,m));
		printf("%d\n",_iCnt2);
		
	}
}

int main(int argc,char* argv[])
{
	process();
	getchar();
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值