时隔一年了,又重新学了逆元。
首先来说一下逆元的概念,逆元,什么是逆元呢,设c是b的逆元,那么b*c%m=1,一个数字是另一个数字的逆元是在另一个数字的前题下的,就是一说谁是谁的逆元,必须是在一个数字的前提下的,逆元和倒数差不多,但是又不是倒数,
再说一下逆元的作用,逆元是用来干什么的,怎么说呢,下想一个问题吧,求 (a/b)%m这个式子的结果,是不是无从下手,我们假如c是b的逆元,那么b*c%m=1,下面我们开始证明一个结论,一个数字除以另一个数字再取余的结果等于这个数乘以除数的逆元再取余。
证明:
设c是b的逆元,则有b*c≡1(mod m);
则(a/b)%m = (a/b)*1%m = (a/b)*b*c%m = a*c(mod m);
这就证明好了,现在基本上都知道逆元是干什么用了吧,下面我们的任务就是求逆元,先简单的介绍一些比较经典的求逆元的方法。
1:费马小定理。
既然有费马小定理那是不是也有费马大定理呢,这个吗,在这里就不做研究了,下面就说一下费马小定理,
内容:假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p),即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
上面结论就是 a^(p-1)%p=1;
费马定理的应用:
假如我们现在求 (a/b)%m=?
设c是b的逆元 即 b*c%m=1;
所以 (a/b)%m=a*c%m;
现在我们的任务就是求c,即b的逆元,
假如b为质数,且gcd(b,m)=1;
由费马小定理可得:b^(m-1)%m=1;
得b*b^(m-2)%m=1;
又b*c%m=1;
所以我们可以得到 c=b^(m-2)%m;
现在我们就可以知道逆元怎么求了,其实说白了求一个数字的逆元就转化成快速幂了,下面 给出实现的c语言代码。
#include<stdio.h>
using namespace std;
const int mod=5;
int A(int x,int y)
{
int res=1;
while(y)
{
if(y%2!=0) res=res*x%mod;
x=x*x%mod;
y/=2;
}
return res;
}
int main()
{
int a,b;
scanf("%d %d",&a,&b);
int c;
c=A(b,mod-2);
int sum;
sum=(a%mod)*(c%mod)%mod;
printf("%d\n",sum);
return 0;
}
2 :扩展欧几里得求逆元:
在这里就不在证明了,因为过程不太好理解,而且也比较复杂,这里就直接给模板吧。
可扩展欧几里得求逆元ax≡1(mod n)其中a,n互质;
下面直接给模板,
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:求逆元表
这个模板的用处就是当你需要求一个逆元比较多的时候就可以打表,把关于p的1~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;