简单理解拓展欧几里得

欧几里得算法

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

它其实用到的是一个定理:
g c d ( a , b ) = g c d ( b , a   m o d   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)
然后根据推论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 我们可以用

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)

(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)
这里可以在进一步用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
递归到尽头是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 <bits/stdc++.h>
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;
}

猜你喜欢

转载自blog.csdn.net/sunmaoxiang/article/details/86746995