定义:如果 a ∗ x = 1 ( m o d p ) , 且 g c d ( a , p ) = 1 a*x = 1(mod p),且gcd(a, p) = 1 a∗x=1(modp),且gcd(a,p)=1(等价于a,p互质),那 x 就为模 p 意义下 a 的乘法逆元。
小定理: ( a / b ) % p = ( a ∗ x ) % p = 1 (a/b) \% p = (a*x) \%p= 1 % p (a/b)%p=(a∗x)%p=1;(x为模p意义下a的逆元)
证明: a / b ∗ b ∗ x ≡ a ∗ x ( m o d p ) 且 b ∗ x = 1 ( m o d p ) a/b * b * x\equiv a * x (mod~p) 且b * x = 1(mod p) a/b∗b∗x≡a∗x(mod p)且b∗x=1(modp);
将除法转换成了乘法,就可以直接取模了。
a ∗ x ≡ 1 ( m o d p ) a*x \equiv1(mod ~p) a∗x≡1(mod p)
扩展欧几里德算法
在这篇博客举的例子就是关于上面这个公式的求解。
贴份代码。
#include
using namespace std;
typedef long long ll;
void exgcd(ll a, ll b, ll &x, ll &y) {
if(!b) {
x = 1, y = 0;
return ;
}
exgcd(b, a%b, x, y);
ll temp = x;
x = y;
y = temp - a/b*y;
}
int main() {
ll n, m, x, y;
scanf("%lld%lld", &n, &m);
exgcd(n, m, x, y);
printf("%lld", (x%m + m)%m);
return -0;
}
费马小定理: p 是 素 数 , a 是 整 数 , g c d ( a , p ) ≡ 1 ( m o d p ) = > a p − 1 ≡ 1 ( m o d p ) p是素数,a是整数,gcd(a,p) \equiv1(mod ~p)=>a^{p-1}\equiv1(mod ~p) p是素数,a是整数,gcd(a,p)≡1(mod p)=>ap−1≡1(mod p)
逆元定义: a ∗ x = 1 ( m o d p ) , 且 g c d ( a , p ) = 1 = > x 是 a 在 模 p 下 的 逆 元 a *x = 1(mod p),且gcd(a, p) = 1=>x是a在模p下的逆元 a∗x=1(modp),且gcd(a,p)=1=>x是a在模p下的逆元
可以看出 a p − 2 a^{p-2} ap−2就是a在模p下的逆元
求个快速幂就行了。
ll Power(ll x, ll y) {
ll n = y, ans = 1, res = x;
while(n){
if(n & 1) ans = ans * res % mod;
n >>= 1;
res = res * res % mod;
}
return ans%mod;
}
例题:Walk
用阶乘的乘除法求组合数,除法取模要用到逆元。
费马小定理只适用于p为素数的时候,当p不是素数时就可以用上欧拉定理了。
欧拉定理(Euler’s theorem): g c d ( a , p ) = 1 = > a φ ( p ) ≡ 1 ( m o d p ) gcd(a, p) = 1=>a^{\varphi(p)}\equiv1(mod~p) gcd(a,p)=1=>aφ(p)≡1(mod p)
其中 φ ( p ) \varphi(p) φ(p)表示的是小于p且和p互质的数的个数。假如p是质数,那么 φ ( p ) \varphi(p) φ(p)就等于 p − 1 p-1 p−1,也就是上面的费马定理。
欧拉函数 φ ( p ) \varphi(p) φ(p)是有通式的(还不会),先贴份链接 欧拉函数的定义与计算
用来求
已知 1 的 逆 元 1 − 1 = 1 1的逆元1^{-1}=1 1的逆元1−1=1,我们将逆元存在inv数组中,inv[1] = 1。
设 k = p / i , r = p % i k=p/i,r=p\%i k=p/i,r=p%i
k ∗ i + r = p = > k ∗ i + r ≡ 0 ( m o d p ) k*i+r=p=>k*i+r\equiv0(mod~p) k∗i+r=p=>k∗i+r≡0(mod p),
在左右同乘 i − 1 , r − 1 = > k ∗ r − 1 + i − 1 = 0 i^{-1},r^{-1}=>k*r^{-1}+i^{-1}=0 i−1,r−1=>k∗r−1+i−1=0
所以 i − 1 = − k ∗ r − 1 < = > i − 1 = − ( p / i ) ∗ ( p % i ) − 1 i^{-1} = -k*r^{-1}<=>i^{-1}=-(p/i)*(p\%i)^{-1} i−1=−k∗r−1<=>i−1=−(p/i)∗(p%i)−1
( p / i ) (p/i) (p/i)是一个实数, ( p % i ) − 1 (p\%i)^{-1} (p%i)−1是小于在第i个逆元之前的逆元,按递推可以求得。
#include
using namespace std;
typedef long long ll;
const ll N = 100007;
ll inv[N] = {0, 1};
int main() {
for(int i=2; i<=N; i++) {
inv[i] = -(N/i) * inv[N%i];//递推关系
inv[i] = (inv[i] % N + N) % N;//确保inv[i] > 0;
}
for(int i=0; i<100; i++) {
cout << inv[i] << ' ';
}
return 0;
}