扩展欧几里得算法

扩欧代码(时间复杂度O(logn))

求ax+by=gcd(a,b)的一组整数解

 

int gcd(int a,int b)
{
	if(b==0)return a;
	return gcd(b,a%b);
}
int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	int x1,y1,d;
	d=exgcd(b,a%b,x1,y1);
	x=y1,y=x1-a/b*y1;
	return d;
}

扩欧的应用

 

求解不定方程

求不定方程ax+by=c的一组整数解

思路

若gcd(a,b)|c(gcd(a,b)是c的因子),则有整数解

先用扩欧求ax+by=gcd(a,b)的解

再乘以c/gcd(a,b),即得原方程的特解

如果a是负数,就a和gcd都取相反数再解

代码

 

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
    
int exgcd(int a,int b,int &x,int &y)
{
  if(b == 0) {x=1, y=0; return a;}
  int x1, y1, d;
  d = exgcd(b, a%b, x1, y1);
  x = y1, y = x1-a/b*y1;
  return d;
}
int main()
{
  int a, b, c, x, y;
  cin >> a >> b >> c;
  int d = exgcd(a,b,x,y);
  if(c%d == 0)printf("%d %d",c/d*x,c/d*y);
  else puts("none");
  return 0;
}

求解同余方程

给定整数a,b,m,求解同余方程ax≡b(mod m),如果x存在整数解,输出任意一个,不存在输出none

思路

把同余方程转化为不定方程,

由ax≡b(mod m)

得ax=m(-y)+b

ax+my=b

代码

 

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
    
int exgcd(int a,int b,int &x,int &y){
  if(b == 0){x = 1, y = 0; return a;}
  int x1, y1, d;
  d = exgcd(b, a%b, x1, y1);
  x = y1, y = x1-a/b*y1;
  return d;
}
int main(){
  int a, b, m, x, y;
  scanf("%d%d%d", &a, &b, &m);
  int d = exgcd(a, m, x, y);
  if(b%d == 0) 
    printf("%d", 1ll*x*b/d%m);
  else puts("none");
  return 0;
}

求解模的逆元

a与m互质时,对于方程ax≡1(mod m),求a的乘法逆元x(0<x<m)

逆元仅在所求数与模数互质时存在

扩展欧几里得算法应该是最优的求逆元算法之一,它和费马小定理具有同样的时间复杂度O(log(n)),但是费马小定理需要模数为质数,扩展欧几里得算法则不需要。

思路

转化为同余方程,等价于ax+my=1

(x%m+m)%m即为答案,这是为了得到最小正整数

ax+my=b 最小整数解的公式:((x0*(b/gcd(a,m)))%(m/gcd(a,m))+(m/gcd(a,m)))%(m/gcd(a,m))

代码

 

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
    
int exgcd(int a,int b,int &x,int &y){
  if(b == 0){
    x = 1, y = 0; return a;
  }
  int x1, y1, d;
  d = exgcd(b, a%b, x1, y1);
  x = y1, y = x1-a/b*y1;
  return d;
}
int main(){
  int a, m, x, y;
  scanf("%d%d", &a, &m);
  exgcd(a, m, x, y);
  printf("%d", (x%m+m)%m);
  return 0;
}

 


求n以内所有正整数模p的逆元

洛谷例题传送门,题解更详细 洛谷P3811

还有一道类似的题也可以看看 洛谷P5431

由于n以内所有正整数都在模p意义下有逆元,所以p和所有数互质,p为质数

设invi为i的逆元,则有递推式衍生出的代码:

inv[1] = 1;
for(int i = 2; i < p; ++ i)
    inv[i] = (p - p / i) * inv[p % i] % p;

阶乘逆元

我们还可以O(n)求出阶乘逆元,因为有如下递推关系

inv[i+1]=1/(i+1)!

inv[i+1]*(i+1)=1/i!=inv[i]

所以我们可以先求出n!的逆元,逆推求出(n-1)! 、(n-2)!等的逆元

posted @ 2023-12-10 18:08  modemingzi  阅读(49)  评论(0)    收藏  举报