请不了解扩展欧几里得算法的同学们提前翻阅书本或自行百度了解。
有两个数a,b,对它们进行辗转相除法,可得它们的最大公约数——这是众所周知的。那么从书本上得知我们从后往前去替代余数,那么最后得到的gcd(a,m)=1=xa+ym中的系数x就是我们想要的a-1mod m(这里我们都默认a有逆,即a,m互素)。为什么这么说呢?
aa-1=1(mod m),那么aa-1-1=km,即a-1a+(-k)m=1,这里的a-1和-k就是我们所说的x、y。那么我们收集辗转相除法中产生的式子,倒回去最后的结果就是xa+ym=1,则x就是a-1。
其实实现代码我也不会写,尤其是只有神才能够掌握的递归算法,像我这样数理基础稀碎的菜鸟来说简直不敢去想。因此我从网上找了几个比较容易理解的算法,并对其进行一些分析。
方法一:
递归求解比较简便,思路如下:
我们用x、y来表示gcd(m,a)=xm+ya中的系数,易知我们有gcd(m,a)=gcd(a,m%a)=x1a+y1(m%a),又m%a=m-(m/a)a,则gcd(m,a)=xm+ya=x1a+y1*(m-(m/a))a=y1m+(x1-(a/b)*y1)*a。可以得到x=y1,y=(x1-(a/b)*y1)。现在我们知道某一层的系数就可以知道上一层的系数,因此我们只需要知道最底层的系数,那么也就可以知道最高层的系数了,最后的x就可以求出来了。
下面是代码:
C语言:
#include <stdio.h>
int e_gcd(int a, int b, int* x, int* y)
{
if (b == 0)
{
*x = 1, *y = 0;
return a;
}
else
{
int r = e_gcd(b, a % b, x, y);
/* r = GCD(a, b) = GCD(b, a%b) */
int t = *x;
*x = *y;
*y = t - a / b * *y;
return r;
}
}
int main()
{
int f=0;
int i,j;
int* x= &i;
int* y= &j;
f = e_gcd(5, 3, x, y);
printf("%d %d",*y,*x);
}
python:
def ext_euclid(a, b):
if b == 0:
return 1, 0, a
else:
x, y, q = ext_euclid(b, a % b)
# q = gcd(a, b) = gcd(b, a%b)
x, y = y, (x - (a // b) * y)
return x, y, q
方法二:
也可以采用非递归的方法。假设m=q1a+p1,a=q2p1+p2,p1=q3*p2+0
那么我们用u3、v3来模拟a、m从上除到下的过程,并且在这个过程中,其实系数x是可以通过q1往下计算并收集系数来计算出来的,而u1、v1就是在做这个工作,于是就有下面这段代码。(其实想弄清其中的过程,直接在IDE里面断点调试一下就可以知道了)
python代码:
def ModReverse(a ,m) :
if gcd(a ,m )!=1:
return None
u1 ,u3 = 0 , m
v1 ,v3 = 1 ,a
while v3!=0:
q = u3//v3
v1 ,v3 ,u1 ,u3 = (u1 - q *v1) ,(u3 - q *v3) ,v1 ,v3
return u1 % m
print(ModReverse(3,5))
其实代码什么的都无所谓,丝路最重要。(滑稽)