1、什么是逆元
当求解公式:(a/b)%m 时,因b可能会过大,会出现爆精度的情况,所以需变除法为乘法:
设c是b的逆元,则有b*c≡1(mod m);
则(a/b)%m = (a/b)*1%m = (a/b)*b*c%m = a*c(mod m);
即a/b的模等于a*b的逆元的模;
2、求逆元的几种方法
(1).费马小定理
在p是素数的情况下,对任意整数x都有x^p≡x(mod)p。
如果x无法被p整除,则有x^(p?1)≡1(modp) 。
可以在p为素数的情况下求出一个数的逆元,x?x^(p?2)≡1(modp) ,x^(p?2)即为逆元。
程序代码:
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).扩展欧几里得算法求逆元
扩展欧几里得算法可以参考小白书;
百度百科-乘法逆元中有这样一个例子:
求x,k就是扩展欧几里得算法了吧~
可扩展欧几里得求逆元ax≡1(mod n)其中a,n互质;
复杂度:O(logn);
ll extend_gcd(ll a, ll b, ll &x, ll &y) { if (b == 0) { x = 1, y = 0; return a; } else { ll r = extend_gcd(b, a % b, y, x); y -= x * (a / b); return r; } } ll inv(ll a, ll n) { ll x, y; extend_gcd(a, n, x, y); x = (x % n + n) % n; return x; }
(3).递推法求逆元
证明:
设x = p % a,y = p / a
于是有 x + y * a = p
(x + y * a) % p = 0
移项得 x % p = (-y) * a % p
x * inv(a) % p = (-y) % p
inv(a) = (p – y) * inv(x) % p
于是 inv(a) = (p – p / a) * inv(p % a) % p
然后一直递归到1为止,因为1的逆元就是1
代码:
const int mod = 1000000009; ll inv(ll a) { return a == 1 ? 1 : (mod - mod / a) * inv(mod % a, mod) % mod; }