Loading

乘法逆元

1定义

乘法逆元,是指数学领域群G中任意一个元素a,都在G中有唯一的逆元a‘,具有性质a×a'=a'×a=e,其中e为该群的单位元。
定理:a存在模p的乘法逆元的充要条件是gcd(a,p) = 1
证明: 首先证明充分性 如果gcd(a,p) = 1,根据欧拉定理,aφ(p) ≡ 1 mod p,因此 显然aφ(p)-1 mod p是a的模p乘法逆元。 再证明必要性 假设存在a模p的乘法逆元为ba ≡ 1 mod p 则 1 = ba - qp
根据贝祖定理,这个式子有整数解的充要条件是a和p的最大公约数整除1,故必须要满足gcd(a,p) = 1
举个例子,在模10的意义下,3的逆元为7,因为3
7与1在模10的意义下同余。

2.计算

下面的逆元均指在模运算下的逆元。

2.1费马小定理

根据费马小定理,如果p是质数,有\(a^{p-1}\equiv1(mod p)\),其中a是整数,所以\(a^{p-2}\)为a的逆元,用快速幂即可,注意取模,这个逆元在模p状态下唯一。
代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ll long long
#define ull unsigned long long
#define N 110
#define M 20
using namespace std;

inline ll ksm(ll a,ll b,ll m){
	ll res=1;
	while(b){
		if(b&1) res=(res*a)%m;
		a=(a*a)%m;
		b>>=1;
	}
	return res%m;
}

int main(){
	ll n,p;
	scanf("%lld%lld",&n,&p);
	for(int i=1;i<=n;i++){
		printf("%lld\n",ksm(i,p-2,p));
	}
}

2.2 扩展欧几里得算法

根据扩展欧几里得算法,知道\(a*x\equiv1(mod p)\)(其实等价于\(k*p-a*x=1\))有解,那么可以通过扩展欧几里得算法求出。这里不再赘述。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ll long long
#define ld long double
#define ull unsigned long long
#define N number
#define M number
using namespace std;

inline ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	ll gcd=exgcd(b,a%b,x,y);
	ll tmp=x;
	x=y;
	y=tmp-a/b*y;
	return gcd;
}

int main(){
	ll a,b;
	scanf("%lld%lld",&a,&b);
	ll x,y;
	exgcd(a,b,x,y);
	printf("%lld",(x%b+b)%b);
}

2.3递推求逆元

首先,我们用\(a^{-1}\)表示a的逆元,易知\(1^{-1}=1\equiv1(mod p)\),所以1为1的逆元
还有递推公式证明:设\(q*k+r=p\)
则:\(q*k+r\equiv0(mod p)\)
\(k*r^{-1}+q^{-1}\equiv0(mod p)\)
\(q^{-1}\equiv{-k*r^{-1}}(mod p)\)
\(q^{-1}\equiv{(p-k)*r^{-1}}(mod p)\)
由此得出递推公式:inv[i] = (p - p / i) * inv[p % i] % p;(设inv[i]为i的逆元)
代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ll long long
#define ull unsigned long long
#define N 4000010
#define M 20
using namespace std;

int inv[N];

int main(){
	ll n,p;
	scanf("%lld%lld",&n,&p);
	inv[1]=1;
	printf("%d\n",inv[1]);
	for(int i=2;i<=n;i++){
		inv[i]=(p-p/i)*inv[p%i]%p;
		printf("%d\n",inv[i]);
	}
	return 0;
}
posted @ 2021-02-02 16:31  hyl天梦  阅读(350)  评论(0编辑  收藏  举报