写在前面
刷题过程中,为了避免高精度的整数运算,常常会要求答案对某个数取模。但这衍生了一个新问题,如果运算过程中涉及了除法,如
a
b
\frac{a}{b}
ba,如果先对a和b取模再做除法,会导致答案错误。比如:
20
5
m
o
d
10
=
4
\frac{20}{5}\ mod\ 10 = 4
520 mod 10=4
但是,
20
m
o
d
10
5
m
o
d
10
=
0
\frac{20\ mod\ 10}{5\ mod\ 10} = 0
5 mod 1020 mod 10=0
为了解决这个问题,我们需要学习新知识:模运算下的乘法逆元。
乘法逆元
什么是逆元呢?百度百科告诉我们:逆元是指一个可以取消另一给定元素运算的元素。
比如,在实数的加法中,任意实数 a
和它的相反数 -a
,互为加法逆元。因为对于任意实数 x
,加上a
之后,再加上-a
,仍然等于 x
,换言之,-a
取消了x
和a
的加法运算。
再比如,在实数的乘法中,任意不为0的实数b
和它的倒数
1
b
\frac{1}{b}
b1,互为乘法逆元。因为对于任意实数x
,乘上b
之后,在乘上
1
b
\frac{1}{b}
b1,仍然等于x
,换言之,
1
b
\frac{1}{b}
b1取消了x
和 b
的乘法运算。
接下来看看模运算下的乘法逆元定义:如果两个整数a
和 b
满足
(
a
∗
b
−
1
)
m
o
d
p
=
0
(a*b-1)\ mod\ p = 0
(a∗b−1) mod p=0,即
a
∗
b
≡
1
(
m
o
d
p
)
a*b≡1(mod\ p)
a∗b≡1(mod p),则称,a
和 b
互为模 p
的乘法逆元。
换言之,如果整数a
, b
, p
满足
a
∗
b
≡
1
(
m
o
d
p
)
a*b≡1(mod\ p)
a∗b≡1(mod p),那么 b
可以取消 a
和任意整数x
在模p
时的乘法,即 a
和 b
互为模 p
的乘法逆元。即
x
∗
a
∗
b
≡
x
(
m
o
d
p
)
x*a*b≡x (mod\ p)
x∗a∗b≡x(mod p),证明过程如下:
x m o d p = x ( a ∗ b − 1 ) + x m o d p = ( x ∗ a ∗ b − x ) + x m o d p = x ∗ a ∗ b m o d p \begin{aligned} x\ mod\ p &= x(a*b-1)+x \ mod\ p \\ &= (x*a*b-x)+x \ mod \ p \\ &= x*a*b \ mod \ p \end{aligned} x mod p=x(a∗b−1)+x mod p=(x∗a∗b−x)+x mod p=x∗a∗b mod p
举个实际的例子:比如 10
和 12
互为模7
时的乘法逆元。然后再找个整数,比如 18
,此时有:
18 m o d 7 = 4 18\ mod\ 7 = 4 \\ 18 mod 7=4
另有,
18 ∗ 10 ∗ 12 m o d 7 = 2160 m o d 7 = 4 \begin{aligned} 18 * 10 * 12\ mod\ 7 &= 2160\ mod\ 7 \\ &= 4 \end{aligned} 18∗10∗12 mod 7=2160 mod 7=4
何时存在乘法逆元
结论
先说结论:a
与 p
互质 是 存在a
关于模p
的乘法逆元b
的充分必要条件。
存在逆元 → 互质
证明 a
与 p
互质 是 存在乘法逆元b
的必要条件。不妨先假设 a
与 b
互为模p
时的乘法逆元,且a
与p
的最大公约数为d
,则有:
( a ∗ b − 1 ) m o d p = 0 (a*b-1)\ mod\ p = 0 (a∗b−1) mod p=0
显然,a*b-1
为 p
的 n
倍,n
为整数,则上式转化为:
( a ∗ b − 1 ) = n p (a*b-1)=np (a∗b−1)=np
将等式中的a
与p
用d
代替,则等式转化为:
( x ∗ d ∗ b − 1 ) = n ∗ y ∗ d ( x ∗ d ∗ b − n ∗ y ∗ d ) = 1 ( x ∗ b − n ∗ y ) ∗ d = 1 x ∗ b − n ∗ y = 1 d \begin{aligned} (x*d*b-1)&=n*y*d \\ (x*d*b - n*y*d) &= 1 \\ (x*b - n*y)*d &= 1 \\ x*b - n*y &= \frac{1}{d} \\ \end{aligned} (x∗d∗b−1)(x∗d∗b−n∗y∗d)(x∗b−n∗y)∗dx∗b−n∗y=n∗y∗d=1=1=d1
显然,x*b-n*y
是一个整数,所以仅当d
为1
时,即a
与p
互质时,上述等式才可能成立。
互质 → 存在逆元
证明 a
与 p
互质 是 存在乘法逆元b
的充分条件。
在证明之前先来回忆一下辗转相除法:
g
c
d
(
a
,
p
)
=
g
c
d
(
p
,
a
m
o
d
p
)
\begin{aligned} gcd(a,p) &= gcd(p, a\ mod\ p) \\ \end{aligned}
gcd(a,p)=gcd(p,a mod p)
不妨用r
来表示模运算的结果:
g
c
d
(
a
,
p
)
=
g
c
d
(
p
,
r
1
)
=
g
c
d
(
r
1
,
r
2
)
=
.
.
.
=
g
c
d
(
r
m
−
1
,
r
m
)
=
1
/
/
因
为
a
与
p
互
质
\begin{aligned} gcd(a,p) &= gcd(p, r_1) \\ &= gcd(r_1,r_2) \\ &= ... \\ &= gcd(r_{m-1}, r_m) \\ &= 1\ \ //因为a与p互质 \end{aligned}
gcd(a,p)=gcd(p,r1)=gcd(r1,r2)=...=gcd(rm−1,rm)=1 //因为a与p互质
将上式用除法表示,则有:
a
=
n
1
p
+
r
1
p
=
n
2
r
1
+
r
2
r
1
=
n
3
r
2
+
r
3
.
.
.
=
.
.
.
r
m
−
3
=
n
m
−
1
r
m
−
2
+
r
m
−
1
r
m
−
2
=
n
m
r
m
−
1
r
m
−
1
=
1
r
m
=
0
\begin{aligned} a &= n_1p + r_1\\ p &= n_2r_1 + r_2 \\ r_1 & = n_3r_2 + r_3 \\ ... &= ... \\ r_{m-3} &= n_{m-1}r_{m-2} + r_{m-1} \\ r_{m-2} &= n_{m}r_{m-1} \\ r_{m-1} &= 1\\ r_{m} &= 0 \\ \end{aligned}
apr1...rm−3rm−2rm−1rm=n1p+r1=n2r1+r2=n3r2+r3=...=nm−1rm−2+rm−1=nmrm−1=1=0
由上述的m+2个等式移项可得:
r
1
=
a
−
n
1
p
r
2
=
p
−
n
2
r
1
.
.
.
=
.
.
.
r
i
=
r
i
−
2
−
n
i
∗
r
i
−
1
r
m
−
1
=
r
m
−
3
−
n
m
−
1
r
m
−
2
特
别
的
,
1
=
r
m
−
1
\begin{aligned} r_1 &= a - n_1p \\ r_2 &= p - n_2r_1 \\ ... &= ... \\ r_{i} &= r_{i-2} - n_{i}*r_{i-1} \\ r_{m-1} &= r_{m-3} - n_{m-1}r_{m-2} \\ 特别的,1 &= r_{m-1} \\ \end{aligned}
r1r2...rirm−1特别的,1=a−n1p=p−n2r1=...=ri−2−ni∗ri−1=rm−3−nm−1rm−2=rm−1
将 rm-1,rm-2 … 以及 r1 依次代入
1
=
r
m
−
1
1 = r_{m-1}
1=rm−1
可以得到用 a
,p
以及 ni表示的等式:
a
x
+
p
y
=
1
ax+py=1
ax+py=1
其中,x
与 y
为 n1,n2,n3 … 的加减乘的运算结果,显然为整数。
得证,当 a
与 p
互质时,存在整数x
与y
满足
a
x
+
p
y
=
1
a
x
−
1
=
p
y
(
a
x
−
1
)
m
o
d
p
=
0
a
x
≡
1
(
m
o
d
p
)
\begin{aligned} ax+py &= 1 \\ ax-1 &= py \\ (ax-1)\ mod\ p &= 0 \\ ax &≡ 1\ (mod\ p) \end{aligned}
ax+pyax−1(ax−1) mod pax=1=py=0≡1 (mod p)
即,当 a
与 p
互质时,存在整数x
与 a
互为模p
时的乘法逆元。
如何计算乘法逆元
扩展欧几里得算法
回忆一下欧几里得的递归过程:
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
a
m
o
d
b
)
=
g
c
d
(
b
,
a
−
b
⌊
a
b
⌋
)
\begin{aligned} gcd(a,b) &= gcd(b, a\ mod\ b) \\ &= gcd(b, a - b\lfloor \frac{a}{b} \rfloor) \end{aligned}
gcd(a,b)=gcd(b,a mod b)=gcd(b,a−b⌊ba⌋)
当 a
与 b
互质时,有 x,y,x',y'
满足:
{
a
x
+
b
y
=
1
b
x
′
+
(
a
−
b
⌊
a
b
⌋
)
y
′
=
1
\left\{ \begin{aligned} ax+by=1 \\ bx'+ (a - b\lfloor \frac{a}{b} \rfloor)y' = 1\\ \end{aligned} \right.
⎩⎨⎧ax+by=1bx′+(a−b⌊ba⌋)y′=1
将上式合并移项:
b
x
′
+
(
a
−
b
⌊
a
b
⌋
)
y
′
=
a
x
+
b
y
a
(
x
−
y
′
)
+
b
(
y
−
(
x
′
−
⌊
a
b
⌋
)
y
′
)
=
0
\begin{aligned} bx'+ (a - b\lfloor \frac{a}{b} \rfloor)y'&=ax+by \\ a(x-y') + b(y-(x'-\lfloor \frac{a}{b} \rfloor)y')&=0 \end{aligned}
bx′+(a−b⌊ba⌋)y′a(x−y′)+b(y−(x′−⌊ba⌋)y′)=ax+by=0
令x,y,x',y'
满足下述关系,则对于任意 a
和 b
上述公式都能成立。
{
x
=
y
′
y
=
(
x
′
−
⌊
a
b
⌋
)
y
′
\left\{ \begin{aligned} x &= y' \\ y &= (x'-\lfloor \frac{a}{b} \rfloor)y' \\ \end{aligned} \right.
⎩⎨⎧xy=y′=(x′−⌊ba⌋)y′
😯好巧,这就是我们要寻找的x,y
和x',y'
的计算公式。另外,当 b = 0
,即到达欧几里得算法的递归边界时,令 x = 1, y = 0
即可满足ax+by=1
(此时的a
显然为1)。
于是我们得到了下述代码,最终得到的 x
即为 a
在模 b
时的乘法逆元,当然前提是函数的返回值为1,即 a 与 b 互质。
int exgcd(int a,int b,int &x,int &y)
{
if(b==0) {
x=1;y=0;
return a;
}
int r = exgcd(b,a%b,x,y);
int t = y;
y = x-(a/b)*y;
x = t;
return r;
}
费马小定理 & 快速幂
费马小定理:若 p
为素数,且 gcd(a, p) = 1,则满足
a
p
−
1
≡
1
(
m
o
d
p
)
a
∗
a
p
−
2
≡
1
(
m
o
d
p
)
\begin{aligned} a^{p-1} ≡ 1\ (mod\ p) \\ a*a^{p-2} ≡ 1\ (mod\ p) \end{aligned}
ap−1≡1 (mod p)a∗ap−2≡1 (mod p)
于是,a
在模 p
时的乘法逆元为
a
p
−
2
a^{p-2}
ap−2,可用快速幂求得。
inline int quick_power(int a, int b, int p) {
int ans = 1;
a = (a % p + p) % p;
for (; b; b >>= 1) {
if (b & 1) ans = (a * ans) % p;
a = (a * a) % p;
}
return ans;
}
// quick_power(a, p-2, p) 即为 a 的乘法逆元。
值得注意的是,该算法仅在 p
为素数时有效,而扩欧并没有该限制。
线性求1到n中每个数的逆元
现在要求1到n中每个数i
在模p
时的乘法逆元。
特别的,i = 1时,其乘法逆元i-1为1
。
当 i > 1 时,不妨先设:
{
k
=
⌊
p
i
⌋
j
=
p
m
o
d
i
\left\{ \begin{aligned} k &= \lfloor\frac{p}{i}\rfloor \\ j &= p\ mod\ i \\ \end{aligned} \right.
⎩⎨⎧kj=⌊ip⌋=p mod i
则有:
{
p
≡
0
(
m
o
d
p
)
(
k
∗
i
+
j
)
≡
0
(
m
o
d
p
)
(
k
∗
i
+
j
)
∗
i
−
1
∗
j
−
1
≡
0
(
m
o
d
p
)
k
∗
j
−
1
+
i
−
1
≡
0
(
m
o
d
p
)
−
k
∗
j
−
1
≡
i
−
1
(
m
o
d
p
)
−
⌊
p
i
⌋
∗
(
p
m
o
d
i
)
−
1
≡
i
−
1
(
m
o
d
p
)
\left\{ \begin{aligned} p &≡ 0\ (mod\ p) \\ (k*i +j) &≡ 0\ (mod\ p) \\ (k*i +j)*i^{-1}*j^{-1} &≡ 0\ (mod\ p) \\ k*j^{-1}+i^{-1} &≡ 0\ (mod\ p) \\ -k*j^{-1} &≡i^{-1}\ (mod\ p) \\ -\lfloor\frac{p}{i}\rfloor *(p\ mod\ i)^{-1} &≡i^{-1} \ (mod\ p) \\ \end{aligned} \right.
⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧p(k∗i+j)(k∗i+j)∗i−1∗j−1k∗j−1+i−1−k∗j−1−⌊ip⌋∗(p mod i)−1≡0 (mod p)≡0 (mod p)≡0 (mod p)≡0 (mod p)≡i−1 (mod p)≡i−1 (mod p)
而且,在递推过程中,当我们要求
i
−
1
i^{-1}
i−1时,对于任意的
(
p
m
o
d
i
)
−
1
(p\ mod\ i)^{-1}
(p mod i)−1都是已知的啦(因为 p mod i肯定小于 i),所以可根据
(
p
m
o
d
i
)
−
1
(p\ mod\ i)^{-1}
(p mod i)−1 通过常数次运算得到
i
−
1
i^{-1}
i−1。
i
−
1
≡
{
1
,
i
=
1
−
⌊
p
i
⌋
(
p
m
o
d
i
)
−
1
,
i
>
2
(
m
o
d
p
)
i^{-1}≡\left\{ \begin{aligned} 1&, i = 1 \\ -\lfloor\frac{p}{i}\rfloor(p\ mod\ i)^{-1}&, i > 2 \\ \end{aligned} \right. \ (mod\ p)
i−1≡⎩⎨⎧1−⌊ip⌋(p mod i)−1,i=1,i>2 (mod p)
inv[1] = 1;
for (int i = 2; i <= n; ++i) {
inv[i] = (p-p/i)*inv[p%i]%p;
}
求任意n个整数的乘法逆元
设有 n 个整数,分别为 a1,a2,a3…,对应的逆元为a1-1,a2-1,a3-1…。
设Si 为 a1 到 ai i 个数的乘积,Si-1 为 a1-1 到 ai-1 i 个数的乘积。那么 Si 与 Si-1 互为逆元。证明过程如下:
∏ i = 1 n a i ∗ a i − 1 ≡ 1 m o d p ∏ i = 1 n a i ∗ ∏ i = 1 n a i − 1 ≡ 1 m o d p S n ∗ S n − 1 ≡ 1 m o d p \begin{aligned} \prod_{i = 1}^{n}{a_i*{a_i}^{-1}} &≡ 1\ mod\ p \\ \prod_{i = 1}^{n}a_i*\prod_{i = 1}^{n}{a_i}^{-1} &≡ 1\ mod\ p \\ S_n*{S_n}^{-1} &≡ 1\ mod\ p \\ \end{aligned} i=1∏nai∗ai−1i=1∏nai∗i=1∏nai−1Sn∗Sn−1≡1 mod p≡1 mod p≡1 mod p
接下来,我们可以先求出 S1 到 Sn,然后通过扩展欧几里得或者快速幂求出 Sn 的乘法逆元 Sn-1。
又有
S
i
−
1
∗
a
i
+
1
−
1
∗
a
i
+
1
≡
S
i
−
1
(
m
o
d
p
)
S
i
+
1
−
1
∗
a
i
+
1
≡
S
i
−
1
(
m
o
d
p
)
\begin{aligned} {S_i}^{-1}*{a_{i+1}}^{-1}*a_{i+1} &≡ {S_i}^{-1}\ (mod\ p) \\ {S_{i+1}}^{-1}*a_{i+1} &≡ {S_i}^{-1}\ (mod\ p) \end{aligned}
Si−1∗ai+1−1∗ai+1Si+1−1∗ai+1≡Si−1 (mod p)≡Si−1 (mod p)
换言之,我们可以利用 ai可以取消ai-1乘法运算的性质,由Si-1 计算出 Si-1-1。
这样就可以O(n)的计算出所有的 Si-1。
最后,由Si-1以及Si 计算出所有的 ai-1:
a
i
−
1
≡
S
i
−
1
∗
S
i
−
1
(
m
o
d
p
)
{a_i}^{-1} ≡ S_{i-1}*{S_{i}}^{-1}\ (mod\ p)
ai−1≡Si−1∗Si−1 (mod p)
s[0] = 1;
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] * a[i] % p;
sv[n] = quick_power(s[n], p - 2);
for (int i = n; i >= 1; --i) sv[i - 1] = sv[i] * a[i] % p;
for (int i = 1; i <= n; ++i) inv[i] = sv[i] * s[i - 1] % p;