逆元定义
逆元和我们平时所说的倒数是有一定的区别的,我们平时所说的倒数是指:a*(1/a) = 1,那么逆元和倒数之间的区别就是:假设x是a的逆元,那么 a * x = 1(mod p),也就是只多了一个取余的操作,这个取余的操作,就会保证a的逆元不一定只是a的倒数。那么我们的逆元有什么作用呢?
并且取余还不满足下面式子:( a/b )%p = (a%p / b%p) % p ,那么我们如果遇到b过大必须在中间过程进行取余的操作,那么我们会发现在乘法中满足:(a*b) % p = (a%p * b%p) %p,那么我们只要将上面式子转换为下面乘法的式子就可以了
我们用inv(b)来表示b的逆元,那么他一定满足:b*inv(b) = 1(mod p) ==> b = 1/inv(b) ,那么我们代入上面的除法的式子:(a/b)%p = (a * inv(b)) %p = (a%p * inv(b)%p) % p
这样我们就可以根据逆元来将除法取余的式子转换为乘法取余的式子
下面看一下用快速幂求解逆元的算法
首先听到这么高大上(主要是长)的名字,相信你的内心一定是崩溃的,仿佛见到了高斯定理或者欧拉定理一样(别着急啊,扩欧等下才讲)
但其实蒙哥马利快速模这个算法并没有那么高大上,它的局限性很大,只有在 p 是质数的情况下才可以使用
首先我们设 inv_a 是 a 的逆元 那么根据定义, inv_a * a ≡1 (mod p)
再根据 费马小定理 a^(p-1) ≡1 , 易得 inv_a * a ≡a^(p-1) (mod p)
移项,得: inv_a ≡ a^(p-2)
于是我们得到了快速幂模算法的一个前提条件: inv_a ≡ a^(p-2) (mod p)
看代码:
#include<iostream> using namespace std; typedef long long LL; const int mod=1e9+7; LL Quick_pow(LL a,LL n) { LL ans=1,res=a; while(n) { if(n&1) ans=ans*res%mod; res=res*res%mod; n>>=1; } return ans; } int main() { LL a; cin>>a; LL inv_a=Quick_pow(a,mod-2); cout<<inv_a<<endl; return 0; }