扩展欧几里得和中国剩余定理

贴一个dalao的证明:欧几里德算法与扩展欧几里德算法

1.欧几里得算法 O(logn)

在这里插入图片描述

int gcd(int a,int b)
{
	if(!b)
	{
		return b;
	}
	return gcd(b,a%b);
}
2.贝祖定理

正整数a,b,那么一定存在非零整数x,y使得ax+by=gcd(a,b) 构造法证明——扩展欧几里得算法

贝祖定理扩展:如果 ax+by=n有解,充要条件是 n 是gcd(a,b)的倍数

a是最大公约数的倍数,b是最大公约数的倍数,他们乘以一个系数相加,也应该是gcd(a,b)的倍数

3.扩展欧几里得算法

a x + b y = d ax+by=d ax+by=d d = ( a , b ) = ( b , a % b ) = ( b , a − ⌊ a b ⌋ ∗ b ) = ( b , a − ⌊ a b ⌋ ∗ b + ⌊ a b ⌋ ∗ b ) = ( b , a ) d=(a,b)=(b,a\%b)=(b,a-⌊\frac{a}{b}⌋*b)=(b,a-⌊\frac{a}{b}⌋*b+⌊\frac{a}{b}⌋*b)=(b,a) d=(a,b)=(b,a%b)=(b,abab)=(b,abab+bab)=(b,a)

b y + ( a % b ) x = d by+(a\%b)x=d by+(a%b)x=d
b y + ( a − ⌊ a b ⌋ ∗ b ) x = d by+(a-⌊\frac{a}{b}⌋*b)x=d by+(abab)x=d
a x + b ( y − ⌊ a b ⌋ ∗ x ) = d ax+b(y-⌊\frac{a}{b}⌋*x)=d ax+b(ybax)=d,所以x是不需要变的,只需要y=y-a/b*x;

int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)//如果b为0
	{
		x=1,y=0;
		return a;//(a,0)=a a*x+0*y=a x=1,y=0就是其中一组解
	}
	int d=exgcd(b,a%b,y,x);
	y=y-a/b*x;
	return d;
}

所求的x,y是不唯一的,只要求出了一个 x 0 , y 0 x_0,y_0 x0,y0 就可以求出所有的解

a ( x − b d ) + b ( y + a d ) = d a(x-\frac{b}{d})+b(y+\frac{a}{d})=d a(xdb)+b(y+da)=d

通解: x = x 0 − b d k , y = y 0 + a d k   ( k ∈ Z ) x=x_0-\frac{b}{d}k,y=y_0+\frac{a}{d}k\ (k∈Z) x=x0dbky=y0+dak (kZ)
求解线性同余方程

在这里插入图片描述

输入
2
2 3 6
4 3 5
输出
impossible
7
思路

eg.    2x≡3(mod 6)无解
    4x≡3(mod 5)   x=2;

a x ≡ b   ( m o d   b ) < = > 存 在 整 数 y , 使 得 a x = m y + b < = > a x − m y = b ax≡b\ (mod\ b) <=>存在整数y,使得ax=my+b<=>ax-my=b axb (mod b)<=>y使ax=my+b<=>axmy=b

令y’=y,则有 a x + m y ′ = b ax+my'=b ax+my=b,该方程有解的等价条件是 (a,m)|b

a x + m y ′ = g c d ( a , m ) , b = k ∗ d , d = g c d ( a , m ) ax+my'=gcd(a,m),b=k*d,d=gcd(a,m) ax+my=gcd(a,m)b=kdd=gcd(a,m)

a x + m y ′ = d ax+my'=d ax+my=d可用扩展欧几里得算法来求,再让等式两边同时扩大 b d \frac{b}{d} db

#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>
const int maxn=1e6+5;
const int mod=1e9+7;
typedef long long ll;
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)//如果b为0
	{
		x=1,y=0;
		return a;//(a,0)=a a*x+0*y=a x=1,y=0就是其中一组解
	}
	int d=exgcd(b,a%b,y,x);
	y=y-a/b*x;
	return d;
}
int main()
{
	int n;
    scanf("%d",&n);
	while(n--)//数据量有点大,建议用scanf
	{
		int a,b,m;
		scanf("%d%d%d",&a,&b,&m);
		int x,y;
		int d=exgcd(a,m,x,y);//注意这里应该对应a,m
		ll ans=1ll*b/d*x%m;//注意不能写成x/d*b,因为d不一定能整除x
		if(b%d)
		printf("impossible\n");
		else
		printf("%lld\n",ans);
	}
    return 0;
}

注意ll ans=1ll*b/d*x%m;要1ll*,不然会溢出。

4.中国剩余定理

在这里插入图片描述
所以 m i m_i mi n i n_i ni互质, m i − 1 m_i^{-1} mi1表示 m i m_i mi n i n_i ni的逆

x = a 1 m 1 m 1 − 1 + a 2 m 2 m 2 − 1 + . . . + a k m k m k − 1 x=a_1m_1m_1^{-1}+a_2m_2m_2^{-1}+...+a_km_km_k^{-1} x=a1m1m11+a2m2m21+...+akmkmk1,用扩展欧几里得来求逆( a x ≡ 1   m o d   n ax≡1\ mod\ n ax1 mod n)

上式成立原因:
对于 n 1 n_1 n1来说, m 1 和 n 1 m_1和n_1 m1n1互质, ( m 1 m 1 − 1 )   m o d   n 1 = 1 (m_1m_1^{-1})\ mod\ n_1=1 (m1m11) mod n1=1,后面其他项 ( m i m i − 1 )   m o d   n 1 (m_im_i^{-1})\ mod\ n_1 (mimi1) mod n1均为0,因为 m i m_i mi中包含 n i n_i ni,所以就可以得到第一项 x ≡ a 1   m o d   n 1 x≡a_1\ mod\ n_1 xa1 mod n1

其他同理

例题

在这里插入图片描述

输入
2
8 7
11 9
输出
31
思路

x   m o d   a 1 = m 1 x\ mod\ a_1=m_1 x mod a1=m1   x = k 1 a 1 + m 1 x=k_1a_1+m_1 x=k1a1+m1
x   m o d   a 1 = m 1 x\ mod\ a_1=m_1 x mod a1=m1   x = k 2 a 2 + m 2 x=k_2a_2+m_2 x=k2a2+m2
要满足所有方程首先要满足前两个方程,然后我们可以两两合并地去求解

k 1 a 1 + m 1 = k 2 a 2 + m 2 = > k 1 a 1 − k 2 a 2 = m 2 − m 1 k_1a_1+m_1=k_2a_2+m_2 =>k_1a_1-k_2a_2=m_2-m_1 k1a1+m1=k2a2+m2=>k1a1k2a2=m2m1,已知 a 1 , a 2 , m 2 − m 1 a_1,a_2,m_2-m_1 a1,a2,m2m1,要求 k 1 , k 2 k_1,k_2 k1,k2扩展欧几里得,上式有解等价于 ( a 1 , a 2 ) ∣ m 2 − m 1 (a_1,a_2)|m_2-m_1 (a1,a2)m2m1

一旦我们求出了一组解 k 1 , k 2 k_1,k_2 k1,k2,通解就可表示为 k 1 ′ = k 1 + k a 2 d , k 2 ′ = k 2 + k a 1 d k_1'=k_1+k\frac{a_2}{d},k_2'=k_2+k\frac{a_1}{d} k1=k1+kda2k2=k2+kda1 前面加一个后面加一个,一减就抵消了

x = k 1 a 1 + m 1 = ( k 1 + k a 2 d ) a 1 + m 1 = a 1 k 1 + m 1 + k a 1 a 2 d x=k_1a_1+m_1=(k_1+k\frac{a_2}{d})a_1+m_1=a_1k_1+m_1+k\frac{a_1a_2}{d} x=k1a1+m1=(k1+kda2)a1+m1=a1k1+m1+kda1a2 x的所有解

x 0 = a 1 k 1 + m 1 x_0=a_1k_1+m_1 x0=a1k1+m1 a 1 , a 2 a_1,a_2 a1,a2的最小公倍数 a = a 1 a 2 d a=\frac{a_1a_2}{d} a=da1a2=> x = x 0 + k a x=x_0+ka x=x0+ka(这就是前两个式子合并后的结果)合并n-1次,就可以把n个方程组合成一个了

当只有一个方程时,要求最小非负整数x:
最后要求的就是 x   m o d   a ≡ x 0 x\ mod\ a≡x_0 x mod ax0 x 和 x 0 x和x_0 xx0模a同余,就是求 x 0   m o d   a x_0\ mod\ a x0 mod a的正余数

最后的a会是a1...an的最小公倍数,很有可能会溢出整数范围,所以一般方程式的个数不会太多

#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>
const int maxn=1e6+5;
const int mod=1e9+7;
typedef long long ll;
using namespace std;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)//如果b为0
	{
		x=1,y=0;
		return a;//(a,0)=a a*x+0*y=a x=1,y=0就是其中一组解
	}
	ll d=exgcd(b,a%b,y,x);
	y=y-a/b*x;
	return d;
}
int main()
{
	int n;
    scanf("%d",&n);
	ll x=0,m1,a1;
	cin>>a1>>m1;
	for(int i=0;i<n-1;i++)
	{
		ll a2,m2;
		cin>>a2>>m2;
		ll k1,k2;
		ll d=exgcd(a1,a2,k1,k2);
		if((m2-m1)%d)
		{
			x=-1;
			break;
		}
		//exgcd求的是k1a1-k2a2=d的解,要把右部变成(m2-m1)的解,等式两边同乘(m2-m1)/d
		k1*=(m2-m1)/d;
		ll t=a2/d;//k1'=k1+a2/d*k
		k1=(k1%t+t)%t;//把k1变成这个方程的最小正整数解
		x=k1*a1+m1;//x0=k1a1+m1
		ll a=abs(a1/d*a2);//最小公倍数 a=a1a1/d
		//x=x0+ka   x≡m(%a) 构建新的迭代
		m1=k1*a1+m1;//m1=x0;
		a1=a;
	}
	if(x!=-1)
	x=(x%a1+a1)%a1;//取x%a
	
	cout<<x<<endl;
    return 0;
}

这道题数据比较极限,要让每个k都变得尽可能地小,所以要k1=(k1%t+t)%t;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值