2.2 模乘逆元-扩展欧几里得法

  • Post author:
  • Post category:其他




模乘逆元简介

所谓模乘逆元(modular multiplicative inverse)就是对于数字a,和与它互质的一个正整数m,找出一个x,满足以下关系:





a

x

1
  

(

m

o

d
  

m

)

ax \equiv 1\;(mod\;m)






a


x













1




(


m


o


d




m


)







上面这个公式的意思就是ax除于m的余数是1。公式里的写法叫做同余,就是≡两边的数除于m的余数相同,而1除于其他正整数的余数一定是1。所以x是a对于m的模乘逆元,记为



a

1

a^{-1}







a














1













。这个写法其实有bug,因为里面没有m,哈哈。其实这个问题,等同于这个问题:





a

x

+

m

y

=

1

ax+my=1






a


x




+








m


y




=








1







解这个问题,需要用到扩展欧几里得法。



扩展欧几里得法

欧几里得法仅仅是求出两个数之间的最大公约数。但是有时候(比如在线性丢番图方程中)需要用这两个数表示最大公约数。也就是要求以下方程的解,公式里的g是a和b的最大公约数。





a

x

+

b

y

=

g

ax+by=g






a


x




+








b


y




=








g







所以模乘逆元不过是扩展欧几里得法中gcd为1的特殊情况而已。实现了扩展欧几里得法便可以直接拿来计算模乘逆元。

x和y,必须为整数,可以为负数。但是这是一个二元一次方程,只有一个方程的话会有N个解。如何保证只有一个解呢?先看看递归过程和例子,我再讲如何保证只有一个解。这是一个利用欧几里得法循环解决问题的过程。利用欧几里得法,把方程简化为以下形式:





b

x

1

+

(

a
  

m

o

d
  

b

)

y

1

=

g

bx_1+(a\;mod\;b)y_1=g






b



x










1




















+








(


a




m


o


d




b


)



y










1




















=








g







因为递归到最后,a%b会变成0,结束递归。既然是递归就要找关系,我们要找的是x,y与x1,y1之间的关系。根据取余的定义,我们可以得到一个公式:





(

a

m

o

d

b

)

=

a

a

b

b

(a\,mod\,b)=a-\lfloor\frac{a}{b}\rfloor b






(


a




m


o


d




b


)




=








a



























b














a























b







需要注意的是





a

b

\lfloor\frac{a}{b}\rfloor




















b














a




























代表的是整数除法







(

a

m

o

d

b

)

=

a

a

b

b

(a\,mod\,b)=a-\lfloor\frac{a}{b}\rfloor b






(


a




m


o


d




b


)




=








a



























b














a























b







代入





b

x

1

+

(

a
  

m

o

d
  

b

)

y

1

=

g

bx_1+(a\;mod\;b)y_1=g






b



x










1




















+








(


a




m


o


d




b


)



y










1




















=








g







得到





b

x

1

+

(

a

a

b

b

)

y

1

=

g

bx_1+(a-\lfloor\frac{a}{b}\rfloor b)y_1=g






b



x










1




















+








(


a



























b














a























b


)



y










1




















=








g







化简,得到





a

y

1

+

b

(

x

1

y

1

a

b

)

=

g

ay_1+b(x_1-y_1\lfloor\frac{a}{b}\rfloor)=g






a



y










1




















+








b


(



x










1






























y










1
































b














a























)




=








g







与下面公式对比





a

x

+

b

y

=

g

ax+by=g






a


x




+








b


y




=








g







我们就找到了关系





x

=

y

1

y

=

x

1

y

1

a

b

x=y_1\\ y= x_1-y_1\lfloor\frac{a}{b}\rfloor






x




=









y










1
























y




=









x










1






























y










1
































b














a




























得到递归公式之后,代码就非常容易写了啊。



实例

以a=10,b=6为例子,详细分解这一过程:

第一次化简





a

=

10

b

=

6

6

x

1

+

(

10

m

o

d

6

)

y

1

=

6

x

1

+

4

y

1

=

2

x

=

y

1

y

=

x

1

y

1

10

6

=

x

1

y

1

1

=

x

1

y

1

a=10\\ b=6\\ 6x_1+ (10\,mod\,6)y_1=6x_1+ 4y_1=2\\ x=y_1\\ y=x_1-y_1\lfloor\frac{10}{6}\rfloor=x_1-y_1\cdot1=x_1-y_1\\






a




=








1


0








b




=








6








6



x










1




















+








(


1


0




m


o


d




6


)



y










1




















=








6



x










1




















+








4



y










1




















=








2








x




=









y










1
























y




=









x










1






























y










1
































6














1


0

























=









x










1






























y










1





























1




=









x










1






























y










1

























得出问题为6*x

1

+ 2*y

1

=2,再进行第二次化简





a

1

=

6

b

1

=

4

4

x

2

+

(

6

m

o

d

4

)

y

2

=

=

4

x

2

+

2

y

2

=

2

x

1

=

y

2

y

1

=

x

2

y

2

6

4

=

x

2

y

2

1

=

x

2

y

2

a_1=6\\ b_1=4\\ 4x_2+(6\,mod\,4)y_2==4x_2+2y_2=2\\ x_1=y_2\\ y_1=x_2-y_2\lfloor\frac{6}{4}\rfloor=x_2-y_2\cdot1=x_2-y_2







a










1




















=








6









b










1




















=








4








4



x










2




















+








(


6




m


o


d




4


)



y










2




















=






=








4



x










2




















+








2



y










2




















=








2









x










1




















=









y










2

























y










1




















=









x










2






























y










2
































4














6

























=









x










2






























y










2





























1




=









x










2






























y










2























得出问题为4x

2

+2y

2

=2,再进行第三次化简





a

2

=

4

b

2

=

2

2

x

3

+

(

4

m

o

d

2

)

y

3

=

2

x

3

+

0

y

3

=

2

x

2

=

y

3

y

2

=

x

3

y

3

4

2

=

x

3

y

3

2

a_2=4\\ b_2=2\\ 2x_3+(4\,mod\,2)y_3=2x_3+0y_3=2\\ x_2=y_3\\ y_2=x_3-y_3\lfloor\frac{4}{2}\rfloor=x_3-y_3\cdot2







a










2




















=








4









b










2




















=








2








2



x










3




















+








(


4




m


o


d




2


)



y










3




















=








2



x










3




















+








0



y










3




















=








2









x










2




















=









y










3

























y










2




















=









x










3






























y










3
































2














4

























=









x










3






























y










3





























2







到了这一步,出现了系数0,y

3

有无数种取值,只取y

3

=0。我们前面学过欧几里得公约数算法原理,肯定知道到了这一步,x

3

一定等于1.





y

3

=

0

,

x

3

=

1

\therefore y_3 = 0,x_3=1\\
















y










3




















=








0


,





x










3




















=








1









然后按顺序代回去:





x

2

=

y

3

y

2

=

x

3

y

3

2

x

2

=

0

y

2

=

1

x

1

=

y

2

y

1

=

x

2

y

2

x

1

=

1

y

1

=

1

x

=

y

1

y

=

x

1

y

1

x

=

1

y

=

2

\because x_2=y_3\\ y_2=x_3-y_3\cdot2\\ \therefore x_2=0\\ y_2= 1\\ \because x_1=y_2\\ y_1=x_2-y_2\\ \therefore x_1 = 1\\ y_1=-1\\ \because x=y_1\\ y=x_1-y_1\\ \therefore x=-1\\ y= 2\\
















x










2




















=









y










3

























y










2




















=









x










3






























y










3





























2




















x










2




















=








0









y










2




















=








1




















x










1




















=









y










2

























y










1




















=









x










2






























y










2




































x










1




















=








1









y










1




















=











1



















x




=









y










1
























y




=









x










1






























y










1



































x




=











1








y




=








2









所以结果为



1

10

+

2

6

=

2

-1\cdot10+2\cdot6=2









1













1


0




+








2













6




=








2





。可以看到结果会可能产生负数,所以需要加上N个m,使得它变成正数。



python实现

整个过程可以用python实现,算法首先是要保存(x

0

,y

0

)->(x

1

,y

1

)的递归关系。

而这个关系,只有一个未知数,那就是





a

b

\lfloor\frac{a}{b}\rfloor




















b














a




























所以只需要保存这个数就好了。代码如下:

# _*_ coding:utf-8 _*_

def extend_euclid(a: int, b: int):
    if a < b:
        temp = extend_euclid(b, a)
        return reversed(temp)
    # 保存a/b的系数
    coefficients = []
    while b != 0:
        coefficients.append(a // b)
        a, b = b, a % b
    # 循环代入
    x, y = 1, 0
    coefficients.reverse()
    for i in coefficients:
        x, y = y, x - y * i
    return x, y


def modular_inverse(a: int, m: int):
    x, y = extend_euclid(a, m)
    while x < 0:
        x += m
    return x


if __name__ == '__main__':
    a, m = 3, 7
    x = modular_inverse(3, 7)
    print(f'{a} * {x} % {m} = {a * x % m}')

测试结果:

3 * 5 % 7 = 1



版权声明:本文为m0_66201040原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。