详解逆元

今天我们聊一聊逆元这个东西。

那么逆元是个什么东西呢?我们可以发现(a/b)%p=((a%p)/(b%p))%p是错的。(显然)

但如果我们一定要求(a/b)%p怎么办呢?这时就需要逆元了。

我们引入逆元的概念。

a*x=1(mod p),我们就把x看成a的倒数,只不过加了一个求余条件,所以x叫做a关于p的逆→→→→→→→→元。

a的逆元,我们用inv(a)来表示,那么(a/b)%p=(a*inv(b))%p=(a%p*inv(b)%p)%p(a与p互质)。这样就把除法,完全转换为乘法了。

那么逆元怎么求呢?我们来看3种方法。

一、费马小定理

a^(p-1)≡1(mod p)两边同除以a→a^(p-2)≡inv(a)(mod p)

所以inv(a)=a^(p-2)(mod p)

这个用快速幂求一下,复杂度O(logn)

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll power(ll a,ll b,ll p)
{
	ll ans=1;
	while(b)
	{
		if(b&1)ans=(ans*a)%p;
		a=(a*a)%p;
		b>>=1;
	}
	return ans;
}
ll Fermat(ll n,ll p)
{
	return power(n,p-2,p);
}
int main()
{
	ll n,p;
	scanf("%lld%lld",&n,&p);
	printf("%lld\n",Fermat(n,p));
	return 0;
}

方法二:

要用扩展欧几里德算法

对于a*x+b*y=1如果ab互质,则有解,这个解的x就是a关于b的逆元,y就是b关于a的逆元。

Why?

(a*x)%b+(b*y)%b=1%b

(a*x)%b=1%b

a*x=1(mod b)

所以x是a关于b的逆元,反之可证明y。

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll exgcd(ll a,ll b,ll &x,ll &y,ll &d)
{
	if(b==0)x=1,y=0,d=a;else
	{
		exgcd(b,a%b,y,x,d);
		y-=x*(a/b);
	}
}
ll inv(ll n,ll p)
{
	ll x,y,d;
	exgcd(n,p,x,y,d);
	return d==1?(x%p+p)%p:-1;
}
int main()
{
	ll n,p;
	scanf("%lld%lld",&n,&p);
	printf("%lld\n",inv(n,p));
	return 0;
}

方法三:

当p是个质数的时候有

inv(a)=(p-p/a)*inv(p%a)%p

证明:

设x=p%a,y=p/a

于是有x+y*a=p

(x+y*a)%p=0

移项得x%p=(-y)*a%p

x*inv(a)%p=(-y)%p

inv(a)=(p-y)*inv(x)%p

于是inv(a)=(p-p/a)*inv(p%a)%p

然后一直递归到1为止,因为1的逆元就是1

Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll inv(ll n,ll p)
{
	return n==1?1:(p-p/n)*inv(p%n,p)%p;
}
int main()
{
	ll n,p;
	scanf("%lld%lld",&n,&p);
	printf("%lld\n",inv(n,p));
	return 0;
}

这个方法不限于求单个逆元,比前两个好,它可以在O(n)的复杂度内算出n个数的逆元

Code:
#include<bits/stdc++.h>
#define ll long long
#define N 3000005
using namespace std;
ll inv[N];
int main()
{
	ll n,p;
	scanf("%lld%lld",&n,&p);
	inv[1]=1;puts("1");
	for(int i=2;i<=n;i++)
	{
		inv[i]=(p-p/i)*1ll*inv[p%i]%p;
		printf("%lld\n",inv[i]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_34531807/article/details/79476845