数论之扩展欧几里德

欧几里德

求整数a,b的最小公约数gcd(a,b)的算法。

递归代码:

#include <iostream>
using namespace std;
#define ll long long
ll gcd(ll a,ll b)
{
    if(b==0)return a;
    else return gcd(b,a%b);
}
int main()
{
    ll a,b;
    while(cin>>a>>b)
    cout<<gcd(a,b)<<endl;
    return 0;
}
/*
1 5
1
2 4
2
60 156
12

*/

扩展欧几里德

对于不完全为0的非负整数a,b,gcd(a, b)表示a, b的最大公约数,必定存在整数对x,y,满足a∗x+b∗y==gcd(a,b) ,有多解是一定的。

gcd(a,b)=gcd(b,a mod b) ①

a*x+b*y=gcd(a,b)  ②

对①式:当b=0时,ax+by=gcd(a,b)=a,显然(x=1,y=0)是方程的一个解。

对②式:当b不为0时,根据欧几里得定理(1式)gcd(a,b)=gcd(b,a mod b)可得 

x和y是gcd(a,b)=ax+by的解,而x′和y′是在对gcd(a,b)按欧几里德算法进行一步后的结果对应的贝祖等式gcd(b,a mod b)=bx'+(a mod b)y'的解.也就是说,gcd(a,b)对应的贝祖等式的解x,y可以由gcd(b,a mod b)对应等式的解x′,y′计算得出. 

由于欧几里德算法最后一步为gcd(d,0)=d,此时对应的等式的解为x=1,y=0,因此只要如上述代码,从gcd(d,0)往前处理,在进行欧几里德算法的递归的时候根据相邻两次调用间x,y和x’,y’的关系计算即可求出ax+by=gcd(a,b)的解.

关系是:

        x = y’

        y = x’ – a/b*y’

更进一步,对于任意不定式ax′+by′=c,只需要在等式ax+by=gcd(a,b)=d两边乘上c/d即可得到解为x′=x∗c/d,y′=y∗c/d

扩展欧几里德代码: 

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

}

扩展欧几里德有什么用处呢?

    求解形如 a*x +b*y = c 的通解,但是一般没有谁会无聊到让你写出一串通解出来,都是让你在通解中选出一些特殊的解,比如一个数对于另一个数的乘法逆元.

     什么叫乘法逆元?

    

    这里,我们称 x 是 a 关于 m 的乘法逆元。

上式可以等价于这样的表达式: a*x + m*y = 1

如果a、m互质,有解,这个解的x就是a关于m的逆元,y就是b关于a的逆元

why?

两边同时求余m

a*x % m + m*y % m = 1 % m

a*x % m = 1 % m  

a*x = 1 (mod m)  

得出:x就是 a关于 m的 逆元, 同理两边同时求余 a --->m*y=1(mod a)   ---> y就是m关于a的逆元

扩展欧几里得求乘法逆元必须保证 gcd(a, m) = 1

接着乘法逆元讲,一般,我们能够找到无数组解满足条件,但是一般是让你求解出最小的那组解,怎么做?我们求解出来了一个特殊的解 x0 那么,我们用 x0 % m其实就得到了最小的解了。为什么?

可以这样思考:

    x 的通解不是 x0 + m*t 吗?

    那么,也就是说, a 关于 m 的逆元是一个关于 m 同余的,那么根据最小整数原理,一定存在一个最小的正整数,它是 a 关于m 的逆元,而最小的肯定是在(0 , m)之间的,而且只有一个,这就好解释了。

   由于问题的特殊性,有时候我们得到的特解 x0 是一个负数,还有的时候我们的 m 也是一个负数这怎么办?

    当 m 是负数的时候,我们取 m 的绝对值就行了,当 x0 是负数的时候,他模上 m 的结果仍然是负数(在计算机计算的结果上是这样的,虽然定义的时候不是这样的),这时候,我们仍然让 x0 对abs(m) 取模,然后结果再加上abs(m) 就行了,于是,我们不难写出下面的代码求解一个数 a 对于另一个数 m 的乘法逆元:

int cal(int a,int m)
{
    int x,y;
    int gcd=ex_gcd(a,m,x,y);
    if(1%gcd!=0) return -1;
    x*=1/gcd;
    m=abs(m);
    int ans=x%m;
    if(ans<=0) ans+=m;
    return ans;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值