如果 a ∗ x ≡ 1 ( m o d p ) a*x≡1\ (mod\ p) a∗x≡1 (mod p),那么, x x x就是 a a a的逆元,相互的, a a a也是 x x x的逆元。那么逆元有什么用呢?
首先我们需要知道,取余运算对除法是不适用的,
a
b
%
p
≠
a
%
p
b
%
p
%
p
\frac{a}{b}\ \%\ p ≠ \frac{a\ \%\ p}{ b\ \%\ p}\ \%\ p
ba % p=b % pa % p % p。
如果我们要计算若干个数相乘起来除以某个数时,并不能像乘法那样边乘边取余,所以我们可以将
a
b
%
p
\frac{a}{b}\ \%\ p
ba % p转化为
a
∗
b
−
1
%
p
a*b^{-1}\ \%\ p
a∗b−1 % p (
b
−
1
b^{-1}
b−1是指
b
b
b在模
p
p
p下的逆元,不是
b
b
b的
−
1
-1
−1次方),这样就可以边乘边取余。
逆元的求法:
方法一:欧拉函数
int power(int a,int b,int p)
{
int res=1%p;
while(b) { if(b&1) res=res*a%p; a=a*a%p; b>>=1; }
return res;
}
int inverse(int a,int p)
{
int phi=a,x=a;
for(int i=2;i<=x/i;i++)
if(x%i==0)
{
phi=phi/i*(i-1);
while(x%i==0) x/=i;
}
if(x>1) phi=phi/x*(x-1);
return power(a,phi-1,p);
}
方法二:扩展欧几里得
int exgcd(int a,int b,int &x,int &y)
{
if(b==0) { x=1; y=0; return a; }
int g=exgcd(b,a%b,y,x);
y-=a/b*x;
return g;
}
int inverse(int a,int p)
{
int x,y;
int g=exgcd(a,p,x,y);
return g==1?(x%p+p)%p : -1; //x有可能是负数,转化为正数
}
update
2021/9/29
又发现了一种神奇的算法,线性递推求逆元,不过适应范围很狭窄,在特定题目中使用,如LuoguP3811
该算法用于求一连串数字对于一个
m
o
d
p
mod\ p
mod p的逆元。
给你一个
n
n
n和
p
p
p,要你算出
1
1
1 ~
n
n
n的所有数对
p
p
p的逆元,前面两种做法会超时。
具体求法是:
对于 1 1 1 ~ n n n中的任意一个数 i i i,设 p = k ∗ i + r p=k*i+r p=k∗i+r, ( k = p i , r = p % i ) (k=\frac{p}{i},r=p\%i) (k=ip,r=p%i)。
显然有 k ∗ i + r ≡ 0 ( m o d p ) k*i+r\equiv0\ (mod\ p) k∗i+r≡0 (mod p)
设 i − 1 i^{-1} i−1和 r − 1 r^{-1} r−1分别是 i i i和 r r r的模 p p p下的逆元。
那么
i
−
1
r
−
1
(
k
∗
i
+
r
)
≡
0
(
m
o
d
p
)
i^{-1}r^{-1}(k*i+r)\equiv0\ (mod\ p)
i−1r−1(k∗i+r)≡0 (mod p)
k r − 1 + i − 1 ≡ 0 ( m o d p ) kr^{-1}+i^{-1}\equiv0\ (mod\ p) kr−1+i−1≡0 (mod p)
i − 1 ≡ − k r − 1 ( m o d p ) i^{-1}\equiv-kr^{-1}\ (mod\ p) i−1≡−kr−1 (mod p)
i − 1 ≡ − p i ∗ ( p % i ) − 1 ( m o d p ) i^{-1}\equiv-\frac{p}{i}*(p\%i)^{-1}\ (mod\ p) i−1≡−ip∗(p%i)−1 (mod p)
为了保证 i − 1 > 0 i^{-1}>0 i−1>0,这里我们对等式右边加上一个 p ∗ ( p % i ) − 1 p*(p\%i)^{-1} p∗(p%i)−1,因为是对 p p p取模,这里加上一个 p p p的倍数,对结果是没有影响的。得到:
i − 1 ≡ ( p − p i ) ∗ ( p % i ) − 1 ( m o d p ) i^{-1}\equiv(p-\frac{p}{i})*(p\%i)^{-1}\ (mod\ p) i−1≡(p−ip)∗(p%i)−1 (mod p)
于是乎,就得到了逆元的递推公式, i i i的逆元就可以由 p % i p\%i p%i的逆元推算出来。
i n v [ i ] = ( p − p i ) ∗ i n v [ p % i ] % p inv[i]=(p-\frac{p}{i})*inv[p\%i]\%p inv[i]=(p−ip)∗inv[p%i]%p
P 3811 代 码 P3811代码 P3811代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int N = 3e6 + 10;
LL n, p, inv[N];
int main()
{
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
scanf("%lld%lld", &n, &p);
inv[1] = 1;
for(int i = 2; i <= n; i ++)
inv[i] = (p - p / i) * inv[p % i] % p;
for(int i = 1; i <= n; i ++) printf("%lld\n", inv[i]);
return 0;
}