乘法逆元及逆元求法

前置知识

模运算:取余运算,即a除以b得到的余数,记为mod,又记为%

模运算过程中,加减乘都可以先对a,b进行%p,然后再进行加减乘,最后再%p,结果不变

运算符优先级,模运算和乘除法的运算符优先级是一样的

同余:a和b除以p得到的余数相同,即p可以整除(a-b)

乘法逆元的问题背景

求解\(\cfrac{a}{b} \mod p\)的值,因为除法不能直接对a,b先取模再进相除,所以这边就引入了逆元

逆元,可以理解为是在mod p意义下b的倒数,下文中将b的逆元记录为inv[b]
以下是逆元的定义
\(b \times inv[b] \equiv 1 \pmod p\)

逆元的性质及其证明

1.第一个性质
\(\cfrac{a}{b} \equiv a \times inv[b] \pmod p\)
证明如下
\(b \times inv[b] \equiv 1 \pmod p\)
\((b \times inv[b]-1)\bmod p=0\)
由模运算的乘法性质\(a \times b \bmod p=(a \bmod p) \times (b \bmod p)\bmod p\)
得到\(\cfrac{a}{b}\times(b \times inv[b]-1)\bmod p=0\)
\((a \times inv[b]- \cfrac{a}{b})\bmod p=0\)
\(a \times inv[b] \equiv \cfrac{a}{b} \pmod p\)
2.唯一性
设b有两个逆元c,d
\(a*c \equiv a*d \pmod p\)
\((a*c-a*d)\pmod p=0\)
\(a(c-d) \mod p=0\)
\(a\% p \neq 0\)
由模运算的乘性质得到\(c=d\)
所以b的逆元唯一
3.可积性
\(inv[a] \times inv[b]=inv[a \times b]\)
证明
\(a*b * inv[ab]\equiv \pmod p\)
又因为\(a*inv[a]\equiv 1\)
\(b*inv[b]\equiv 1\)
模运算乘性质易证可积性成立
4.周期性
求解\(\cfrac{a}{b} \mod p\)中b的逆元
一般默认b小于p,但是当b大于p的时候来求逆元,其实就是一个周期性
\(inv[k*p+r]=inv[r],0<r<p,k \in N^*\)
证明
因为同余,满足两边同乘以一个数依旧成立
所以有以下
\(\cfrac{n}{r} \equiv k_1 \pmod p\)
\(\cfrac{n}{tp+r} \equiv k_2 \pmod p\)
则对于第二个式子,两边同时乘以\(tp+r\)
\(n \equiv k_2r \pmod p\)
\(\cfrac{n}{r} \equiv k_2 \pmod p\)
同理\(\cfrac{n}{r} \equiv k_1 \pmod p\)
\(k_1 \equiv k_2 \pmod p\)
\(b=r,c=t*p+r\),则有
\(\cfrac{n}{b} \equiv \cfrac{n}{c} \pmod p\)
\(n*inv[b] \equiv n*inv[c] \pmod p\)
\(inv[b] \equiv inv[c] \pmod p\)
又因为\(0\le inv[b],inv[c] <p\)
所以\(inv[b]=inv[c]\)

逆元求法

费马小定理和拓展欧几里得这边就不讲了
这边主要讲线性求逆元
一下模的结果范围为[0,r-1]
\(p=k*i+r\)
\(k*i+r \equiv 0 \pmod p\)
模运算乘性质有
式子两边同时乘以\(inv[i]*inv[r]\)
\(k*inv[r]+inv[i] \equiv 0 \pmod p\)
\(inv[i] \equiv k*inv[r] \pmod p\)
\(inv[i] \equiv -\cfrac{p}{i}*inv[p \% i] \pmod p\)
其中p%i在[0,i-1]范围内,即求i的时候p%i的逆元已经被求出
1.线性求逆元
原题见,洛谷P3811乘法逆元模版题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx=20000999;
ll inv[mx];
void getinv(ll n,ll p){
	inv[1]=1;
	for(int i=2;i<=n;i++){
		inv[i]=(1ll*(p-(p/i))*inv[p%i])%p;
		//因为C++对于负数取模还是负数所以需要+p 
		//中间相乘可能会爆int范围,所以用ll 
	}
}
int main(){
	int n,p;
	scanf("%d%d",&n,&p); 
	getinv(n,p);
	for(int i=1;i<=n;i++){
		printf("%d\n",inv[i]);
	}
}

2.线性求单个逆元

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx=20000999;
ll inv[mx];
ll getinv(int x,int p) {
	return x==1?1:1ll*(p-p/x)*getinv(p%x,p)%p;
}
int main() {
	int n,p;
	scanf("%d%d",&n,&p);
	cout<<getinv(n,p);
}
posted @ 2021-01-02 09:43  幽灵轩  阅读(405)  评论(0编辑  收藏  举报