搬运自我的CSDN

欧几里德(这个不是重点)

求整数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
*/

在维基百科上看到的图片还有文字,对理解欧几里德很有帮助

辗转相除法的演示动画:
两条线段长分别可表示252和105,则其中每一小分段长代表最大公约数21。如动画所示,只要辗转地从大数中减去小数,直到其中一段的长度为0,此时剩下的一条线段的长度就是252和105的最大公因数。

扩展欧几里德(重点 is coming!!!)

对于不完全为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) ① ax+by=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;

}

 

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

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

什么叫乘法逆元?

这里,我们称 x 是 a 关于 m 的乘法逆元。上式可以等价于这样的表达式: ax + my = 1如果a、m互质,有解,这个解的x就是a关于m的逆元,y就是b关于a的逆元

why?    两边同时求余m    ax % m + my % 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;

}

 


年轻也需要有所作为( ・´ω`・ )