欧几里得及其扩展

一、欧几里德算法又称辗转相除法,用于计算两个正整数a,b的最大公约数

定理:gcd(a,b) = gcd(b,a%b)


ll gcd(ll a,ll b)
{
 if(!b)
  return a;
 return gcd(b,a%b);
 //return b?gcd(b,a%b):a;
}

二、扩展欧几里德:ax+by=c

x=x0+b/gcd(a,b)*t;

y=y0+a/gcd(a,b)*t;

 

ax+by=gcd(a,b)      gcd(a,b)=gcd(b,a%b)

è ax+by=gcd(b,a%b)=bx0+(a%b)y0       a%b=a-a/b*b

è ax+by=ay0+b*(x0-a/b*y0)

è 所以可得出:x=y0     y= x0-a/b*y0

ll exgcd(ll a,ll b,ll &x,ll &y)
{
 if(!b){
  x=1;
  y=0;
  return a;
 }
 ll ans=exgcd(b,a%b,x,y);
 ll temp=x;
 x=y;
 y=temp-a/b*y;
 return ans;
}

三、同余定理:

给定正整数m,a/mb/m所得余数相同,称a、b同余。

ab(mod m),存在整数k,使a=b+km;

a+-*cb+-*c(mod m)

四、逆元

对于正整数a和m,如果有ax≡1(mod m),那么把这个同余方程中x的最小正整数解叫做a模m的逆元。

1、扩展欧几里德求逆元:由同余定理 ax≡1(mod m)可化为

ax=1+km  à  ax-km=1

可直接用扩展欧几里德求出a的逆元x

2、费马小定理求逆元(当m为素数时):ax≡1(mod m) --> x= 

推导过程:

 

五、一元线性同余方程

 

定义:形如axb(mod m),  x是未知整数的同余式称为一元线性同余方程。

 

定理:a,b,m是整数且m>0gcd(a,m)=d,如果d|b(‘|’的意思为整除即b%d==0,则方程恰有d个模m不同余的解否则方程无解。

可以直接用扩展欧几里德求

ll f()//ax≡b(mod m)
{
 ll a,b,m;
 scanf("%lld%lld%lld",&a,&b,&m);
 ll x,y;
 ll d=exgcd(a,m,x,y);
 if(b%d)
  return -1;
 x=x*(b/d)%m;
 for(int i=0;i<d;i++)
  printf("%lld ",(x+i*m/d)%m);
}

 

六、线性同余方程组

Xr1(mod a1)

X≡r2 (mod a2)

X≡r3 (mod a3)

………………

X≡rn (mod an)

 

X=r1+a1*x

X=r2+a2*y

àa1*x-a*2y=r2-r1

由扩展欧几里得通解x=x0+a2/gcd*t

带回原式 X=r1+a1*x0+a1*a2/gcd*t

由同余定理可知X≡r1+a1*x0 (mod a1*a2/gcd)

然后再令r1=r1+a1*x0

  a1=a1*a2/gcd

可将X化为X≡r1 (mod a1)

然后再与后几项依次合并就可得出X。

ll solve()
{
 int n,flag=1;
 ll a1,r1,a2,r2,x0,y0;
 scanf("%d",&n);
 scanf("%lld%lld",&a1,&r1);
 for(int i=1;i<n;i++){
  scanf("%lld%lld",&a2,&r2);
  ll a=a1,b=a2,c=r2-r1;
  ll d=exgcd(a,b,x0,y0);//d=gcd(a1,a2)
  if(c%d)
   flag=0;
  int t=b/d;
  x0=(x0*(c/d)%t+t)%t;//求x0的最小正整数解
  r1=a1*x0+r1;
  a1=a1*a2/d;
 }
 if(!flag)
  return -1;
 return r1;
}

七、中国剩余定理

当线性同余方程组中的m1m2m3..mn两两互素时,则线性同余方程组

Xa1(mod m1)

X≡a2 (mod m2)

X≡a3 (mod m3)

………………

X≡an (mod mn)

有模M=m1*m2*m3..*mn的唯一解。

Mi=M/mi

a1*M1*p1+a2*M2*p2+a3*M3*p3+..+an*Mn*pn就是同余方程组的解。

ll China()
{
 int n;
 ll M=1,a[1005],m[1005];
 ll Mi,x0,y0,ans=0,d;
 scanf("%d",&n);
 for(int i=0;i<n;i++)
  scanf("%lld%lld",&m[i],&a[i]);
 for(int i=0;i<n;i++)
  M*=m[i];
 for(int i=0;i<n;i++){
  Mi=M/m[i];
  d=exgcd(Mi,m[i],x0,y0);
  ans=(ans+Mi*x0*a[i])%M;
 }
 return (ans+M)%M;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值