GCD Extends_GCD 欧几里得算法+扩展欧几里得算法详解

1.欧几里得算法:

我们从小学开始老师都会让我们求解一一种问题,叫做最大公约数,这里的最大公约数就叫做GCD
当然求解最大公约数的算法也是非常的重要,我们在这里就引入欧几里得的算法,也就是著名的辗转相除法
首先给出定义:
GCD(a,b)=GCD(b,a%b)
在这里有的人会问a,b到底哪个大才能使对的,其实在这里我们的a,b是没有大小之分的,要是硬说影响的话,是有那么一点影响,但是无关痛痒
首先这里的生命是最好a>b
但是当a<b的时候,我们会发现a%b=a,也就是说这个算法会自动调换会a>b的状态,所以我们说这一点影响无关痛痒,不影响算法的本质
证明这里我们就不在涉及
但是在算法的实现方面我有一些要说的:
from time import *
def gcd_1(a,b):
    if b==0:
        return a
    else:
        return gcd_1(b,a%b)

def gcd_2(a,b):    #一旦数据量差距过大无法应用,但是在数据量相差不多的情况下,还是很优秀的,总而言之不要用,因为不稳定
    if a==b:
        return a
    elif a>b:
        return gcd_2(a-b,b)
    else:
        return gcd_2(b-a,a)

def gcd_3(a,b):
    if a==b:
        return a
    elif a<b:
        return gcd_3(b,a)
    elif not(a&1) and not(b&1):
        return gcd_3(a>>1,b>>1)<<1
    elif not(a&1) and b&1:
        return gcd_3(a>>1,b)
    elif not(b&1) and a&1:
        return gcd_3(a,b>>1)
    else:
        return gcd_3(a-b,b)

time=clock()
a=eval(input("a:"))
b=eval(input("b:"))
print("gcd_1:%d"%gcd_1(a,b))
print("time gcd_1:%lf"%(clock()-time))
#time=clock()
#print("gcd_2:%d"%gcd_2(a,b))
#print("time gcd_2:%lf"%(clock()-time))
time=clock()
print("gcd_3:%d"%gcd_3(a,b))
print("time gcd_3:%lf"%(clock()-time))
实验结果:
a:34567890
b:1234567
gcd_1:1
time gcd_1:2.589272
gcd_3:1
time gcd_3:0.032302
很显然,虽然朴素的GCD写的很简洁,但是毫无疑问,朴素的GCD因为取模运算的限制太过,我们的位运算+更相孙减术(gcd_2)结合的优势最明显,在我们的工程项目中最好用gcd_3的写法

2.Extends_gcd扩展欧几里得算法:

我们的欧几里得算法很优秀,但是只用来求解最大公约数好像屈才了,所以这里我们对欧几里得算法进行扩展,我们引入扩展欧几里得算法
在讲解扩展欧几里得算法之前,我们先来讲解个很重要的知识:
1.贝祖定理:
对于任何一组不全是0的两个数x,y总是满足
ax+by=gcd(x,y)这是一定成立的(之后我们还要讲一下不成立的另一个中很相近的定理)
这个解是唯一的
但是想要求解出这个等式的整数解确实非常的麻烦,我们需要不断的尝试,这是没有办法忍受的异常繁琐的工作
所以说,这时候,为了快速求解出这个唯一的解,我们就需要用到扩展欧几里得算法
但是这里请注意,扩展欧几里得算法是基于欧几里得算法的,我们在这里只是引入了逆推 的过程
逆推导:
aX0+bY0=gcd(a,b)=gcd(b,b%a)
gcd(b,a%b)=bX1+(a%b)Y1
....
gcd(a,0)=aXn+0*Yn=a    上面已经提到过了,最后的状态一定是a>b的
这时候我们让Xn=1,Yn=0就可以
逆推导开始:
aX0+bY0=gcd(a,b)=gcd(b,a%b)
gcd(b,a%b)=bX1+(a%b)Y1
gcd(b,a%b)=bX1+(a-b*(a/b))Y1=aY1+b*(X1-a/bY1)=gcd(a,b)
显然,我们可以得到
X0=Y1
Y0=X1-a/bY1
这就是我们的逆推导关系,我们利用C++的引用的手段,可以轻松的实现参数的时时改变,轻而易举实现这个算法
但是小心一点,我们的逆推导的过程是建立在
ax+by=gcd(a,b)上的,也就是说扩展欧几里得算法求出来的解是在等号右边是gcd(a,b)的解,这就引出了我们下面的问题

扩展欧几里得算法的应用:
1.求解不定方程:
ax+by=c
是不是看上去和我们上面的贝祖定理非常的相似?没错这时候我们利用扩展欧几里得求出来ax+by=gcd(a,b)的话,如果这时候c是gcd(a,b)的整数倍的话,我们扩展欧几里得算法得到的解都乘上c/gcd(a,b)得到的就是这个不定方程的解,但是,如果c不是gcd(a,b)的整数倍,说明这个不定方程没有解
为什么?
因为我们的ax+by=gcd(a,b)如果有解的条件就是gcd(a,b)是gcd(a,b)的1倍,但是如果没有说明我们没有办法分解出这个ax+by=gcd(a,b)的式子,那么自然,gcd(a,b)的倍数依旧没有解

2.求解线性同余方程:
我们将ax=b(mod n)(ps:那个等号是三等号,代表同余)满足的解称之为a模b关于n同余,这时候我们根据同余的定义可以得到
n|(ax-b)->ax-b=my->ax-my=b->ax+my=b(m只是一个常数,正负无所谓)
是不是又觉得这个方程眼熟,没错还是扩展欧几里得的不定方程
这时候我们就转化成了求不定方程的问题了,注意这里的同余的结果有很多个
但是这些结果都有一个同性
1.结果总数有gcd(a,n)个
2.自通解以后,每个解相差b/gac(a,n)   //这里我有一个问题,按照程序角度来说,我们会不会少元素的输出?

3.求解模逆元:
模拟元的定义,如果
ax=1(mod n)称x是a的模逆元,这里的条件是a,n必须互素
为什么,我们再转化一下:
ax+ny=1只有我们的gcd(a,n)=1的时候才有解,并且解唯一,gcd(a,n)=1的意思就是互素
所以模逆元存在的条件是a,n必须互素
性质是模逆元唯一
转化一下,这时候我们还是要求一个不定方程

综上,我们的利用欧几里得的问题始终就是求一个不定方程,只不过是不定方程的等号的右边的值不断的改变罢了,只有等号右边的值是gcd的整数倍才有解,有gcd个解,否则没有解

3.Code:

#include"iostream"
#include"cstdio"
#include"cstring"
#include"cstdlib"

using namespace std;

int gcd(int a,int b)   //GCD
{
	return b?gcd(b,a%b):a;
}

int Extends_gcd(int a,int b,int& x,int& y)   //扩展欧几里得算法 
{
	if(b==0)
	{
		x=1;
		y=0;
		return a;
	}
	int g=Extends_gcd(b,a%b,x,y);
	int t=x;
	x=y;
	y=t-(a/b)*y;
	return g; 
} 

int main()
{
	int x;
	int y;
	int a,b,c;
	scanf("%d%d%d",&a,&b,&c);
	int g=Extends_gcd(a,b,x,y);
	printf("GCD:%d\n",g);
	//以下是求出解得,必须要乘倍数,是因为我们当时算的基础以gcd,但是实际上方程的右边是gcd的倍数(有解的话),所以我们要先乘上一个倍数 ,才是真正的解 
	if(c%g) printf("No solution\n");
	else printf("%d*%d+%d*%d=%d\n",a,x*(c/g),b,y*(c/g),c);
	return 0;
} 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值