简单理解拓展欧几里得

欧几里得算法

所谓的欧几里得算法就是辗转相除法,作用是求两个数的最大公约数

它其实用到的是一个定理:
g c d ( a , b ) = g c d ( b , a   m o d   b ) ; gcd(a,b) = gcd(b, a\ mod\ b); gcd(a,b)=gcd(b,a mod b);
《算法导论》上有上式严密的证明,大体就是证明
g c d ( a , b ) ∣ g c d ( b , a   m o d   b ) & & g c d ( b , a   m o d   b ) ∣ g c d ( a , b ) gcd(a,b) | gcd(b, a\ mod\ b) \&\& gcd(b, a\ mod\ b)|gcd(a,b) gcd(a,b)gcd(b,a mod b)&&gcd(b,a mod b)gcd(a,b)
然后根据推论a|b&&b|a 可以说明 |a| = |b| 证明的。

代码:

int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

拓展欧几里得算法

这个算法其实是解类似于ax+by=k*gcd(a,b)这个公式的通解的,其实仅仅用拓欧只能求得一个特解,设特解 x 0 , y 0 x_0 ,y_0 x0,y0我们可以用

x = x 0 + b k t / g c d ( a , b ) y = y 0 − a k t / g c d ( a , b ) x = x_0+bkt/gcd(a, b) \\ y = y_0-akt/gcd(a, b) x=x0+bkt/gcd(a,b)y=y0akt/gcd(a,b)

(t是整数,使得x,y在指定范围)求出在一定范围内的所有解。

知道了拓欧在求不定方程的作用,那如何求那个特解呢?

我们可以把求特解的过程当成辗转相除法的逆运算,本质是个递归
a x 1 + b y 1 = g c d ( a , b ) b x 2 + ( a   m o d   b ) y 2 = g c d ( a , b ) ax_1+by_1=gcd(a,b) \\ bx_2+(a\ mod \ b)y_2 = gcd(a,b) ax1+by1=gcd(a,b)bx2+(a mod b)y2=gcd(a,b)
这里可以在进一步用x2,y2推x1,y1
a x 1 + b y 1 = b x 2 + ( a   m o d   b ) y 2 a x 1 + b y 1 = b x 2 + ( a − ⌊ a b ⌋ b ) y 2 a x 1 + b y 1 = a y 2 + ( b x 2 − ⌊ a b ⌋ b y 2 ) 所 以 : x 1 = y 2 y 1 = x 2 − ⌊ a b ⌋ ∗ y 2 ax_1+by_1=bx_2+(a\ mod\ b)y_2\\ ax_1+by_1=bx_2+(a - \lfloor\frac{a}{b}\rfloor b)y_2\\ ax_1+by_1=ay_2+(bx_2 - \lfloor\frac{a}{b}\rfloor by_2)\\ 所以:\\ x_1 = y_2\\ y_1 = x_2-\lfloor\frac{a}{b}\rfloor*y_2 ax1+by1=bx2+(a mod b)y2ax1+by1=bx2+(abab)y2ax1+by1=ay2+(bx2baby2)x1=y2y1=x2bay2
递归到尽头是b=0然后就可以令x=1,y=0,然后返回a就是gcd的值,有了理论基础代码就不难写出了。

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

Disgruntled Judge

UVA - 12169

这道题暴力可以莽枚举a和b就行,但比较好的方法是根据递推式,只枚举a然后用拓欧推b,然后带入检验。

代码:

#include 
using namespace std;

typedef long long ll;

ll gcdEx(ll a, ll b, ll & x, ll & y) {
    if(!b) {
        x = 1;
        y = 0;
        return a;
    }
    else {
        ll ret = gcdEx(b, a % b, y, x);
        y -= (a/b)*x;
        return ret;
    }
}
const ll mod = 10001;
ll x[mod+10];
int main()
{
    // freopen("/Users/maoxiangsun/MyRepertory/acm/i.txt", "r", stdin);
    ll T;
    scanf("%lld", &T);
    for(ll i = 1; i < 2*T; i+=2) {
        scanf("%d", &x[i]);
    }
    for(ll a = 0; ; a++) {
        ll c = x[3]-a*a*x[1];
        ll b,y;
        // cout << "-0";
        ll g = gcdEx(a+1, mod, b, y);
        if(c % g) continue;
        b = b * c / g;
        bool flag = true;
        for(ll i = 2; i <= 2*T; i++) {
            if(i & 1) {
                if(x[i] != (x[i-1]*a + b)%mod) {
                    flag = false;
                    break;
                }
            }
            else x[i] = (x[i-1] * a + b)%mod;
        }
        if(flag) break;
    }
    for(ll i = 2; i <= 2*T; i+=2) {
        printf("%lld\n", x[i]);
    }
    return 0;
}

你可能感兴趣的:(简单理解拓展欧几里得)