逆元(inv)
1.逆元
若a*x≡1(mod b) ,a,b互质,则称x为a的逆元。
2.应用
(a/b)%m 时,因b可能会过大,会出现爆精度的情况,且除法不满足同余拆分定理。
3.求逆元的方法
(1).费马小定理
在m是素数的情况下,对任意整数都有。
如果无法被整除,则有。
可以在为素数的情况下求出一个数的逆元,,即为逆元。
假设数据范围1<=x<=10^9,p=1000000007,p是素数;
所以x肯定就无法被p整除啊,所以最后就得出x^(p-2)为x的逆元啦。
复杂度O(logn);
代码:
const int mod = 1000000009;
long long quickpow(long long a, long long b) {
if (b < 0) return 0;
long long ret = 1;
a %= mod;
while(b) {
if (b & 1) ret = (ret * a) % mod;
b >>= 1;
a = (a * a) % mod;
}
return ret;
}
long long inv(long long a) {
return quickpow(a, mod - 2);
}
(2)扩展欧几里得算法求逆元
例如:4关于1模7的乘法逆元为多少?
4X≡1 mod 7
这个方程等价于求一个X和K,满足
4X=7K+1
其中X和K都是整数。
求x,k就是扩展欧几里得算法
可扩展欧几里得求逆元ax≡1(mod n)其中a,n互质;
根据逆元的定义,则可以转化为a*x+b*y=1。这样就可以用扩展欧几里得算法求x了。
注意:在gcd不为1说明逆元不存在(因为c=1,c%gcd==0为有整数解的充分必要条件),若为1,调整x0到0~m-1的范围中即可
适用条件:
复杂度:O(logn);
代码:
int ex_gcd(int a,int b,int &x,int &y) //扩展欧几里得
{
if(b==0)
{
x=1;
y=0;
return a;
}
int r=ex_gcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
return r;
}
int mod_reverse(int a,int b)//a*x=1(mod b) 求a的逆元x
{
int d,x,y;
d=ex_gcd(a,b,x,y);
if(d==1)
return (x%b+b)%b;
else
return -1;
}
(3) 逆元线性筛 ( P为质数 )
求1,2,...,N关于P的逆元(P为质数)
复杂度:O(N)
代码:
const int mod = 1000000009;
const int maxn = 10005;
int inv[maxn];
inv[1] = 1;
for(int i = 2; i < 10000; i++)
inv[i] = inv[mod % i] * (mod - mod / i) % mod;
如果是求阶乘的逆元呢?(阶乘数组:fac[ ])
代码:
inv[maxn]=mod_pow(fac[maxn],mod-2);
for(ll i=maxn-1;i>=0;i--)
inv[i]=(inv[i+1]*(i+1))%mod;
(4)
但是你会发现费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求与互素。实际上我们还有一
种通用的求逆元方法,适合所有情况。公式如下
现在我们来证明它,已知,证明步骤如下