BSGS(baby step gaint step)+快速冪+exgcd逆元

  1. 一個神奇的算法, 快速求出 A^x=B(mod C),C是素數,求最小的非負x值 若x有解,那麼 0<=x < C, 爲什麼?

    由費馬小定理可以得a^(c-1) = 1(mod c)
    那麼0~c-1必定是一個循環節(不一定是最小的)。既然是%c,那麼B一定是0到c-1之間的一個數。最壞的條件下,a的c-1以內次方%c的餘數各不相同,那麼在0~C-1時一定存在一個x滿足條件。
    那麼不是最壞情況時, 就能找到一個更小的在0~c-1的循環節。

    BSGS的算法是這樣的:

    首先取m=sqrt(c)向上取整。(爲什麼取sqrt(c)?我也不是很懂)

    然後先預處理a的0到m次方。

    a^x=b ( %c ) 設x=i*m+j; 即: i爲x/m,j爲x%m。 a^(i*m+j)=b; b * (a^(-m))^i =
    a^j ( %c )

    先枚舉j,把右邊存起來(Hash 下一步用二分查找, 或哈希表) 枚舉i,如果左邊的數值曾經存儲過(b * (a^(-m))^i =
    a^j),則 x=i*m+j, 得解。

  2. 快速冪, 其實很好理解, 對於求x^y mod p
    我們可以把每次的乘積存下來, 最後一起累乘, 即
    2^10
    = 2^5 * 2^5
    那麼每次把y>>=1, 如果y是奇數那麼就把這次分出的答案乘進答案
  3. xy mod p = z mod p
    求最小的x, 其實很簡單, 可以將方程變成ax+by=c
    同時除以gcd(a, b), 如果c除不盡那麼無解
    否則變成a0x+b0y=c0;->(a0x+b0y=1;x,y乘以c0)

下面給一個例題;
給出下列三種操作
1、給定 y、z、p,計算 y^z mod p 的值;
2、給定 y、z、p,計算滿足 xy ≡z(mod p)的最小非負整數 x;
3、給定 y、z、p,計算滿足 y^x ≡z(mod p)的最小非負整數 x.
三個操作都在上面講過了下面給出代碼

#include <bits/stdc++.h>
using namespace std;

#define hash Hash
#define rep(i, s, t) for(int i = s; i <= t; ++i)

typedef long long ll;

ll p;
ll calc1(ll x, ll y) {
    ll res = 1LL;
    for(; y; y>>=1) {if(y & 1) res = res*x % p; x = x*x % p;}
    return res;
}

ll gcd(ll a, ll b) {
    while(b) {
        ll t = a;
        a = b;
        b = t%b;
    }
    return a;
}

void exgcd(ll a, ll b, ll &x, ll &y) {
    if(!b) {
        x = 1; y = 0;
    }
    else {
        exgcd(b, a%b, y, x);
        y -= a/b*x;
    }
}

struct HASH_TABLE {
    const ll mod = 100007;
    ll h[100007+10][2];

    ll find(ll x) {
        ll t = x % mod;
        while(h[t][0] != x && h[t][0] != -1) t = (t+1) % mod;
        return t;
    }

    void insert(ll x, ll id) {
        ll t = find(x);
        if(h[t][0] == -1)
            h[t][0] = x, h[t][1] = id;
    }

    ll get(ll x) {
        ll t = find(x);
        return h[t][1];
    }

    void BSGS(ll a, ll b, ll p) {
        memset(h, -1, sizeof(h));
        ll m = ceil(sqrt(p)), t = 1;
        rep(i, 0, m-1) {
            insert(t, i);
            t = t*a%p;
        }
        ll d=1, x, y;
        ll ans = -1;
        rep(i, 0, m-1) {
            exgcd(d, p, x, y);
            x = ((x*b) % p + p)%p;
            y = get(x);
            //printf("%lld,%lld\n",x,y);
            if(y ^ -1) {
                ans = i*m+y;
                break;
            }
            d = d*t % p;
        }
        if(ans == -1) puts("Orz, I cannot find x!");
        else printf("%lld\n", ans);
    }
}T;

int main() {
#ifndef ONLINE_JUDGE
    freopen("calc.in", "r", stdin);
    freopen("calc.out", "w", stdout);
#endif
    int _, cmd;
    scanf("%d%d", &_, &cmd);
    while(_--) {
        ll y, z, a, b;
        scanf("%lld%lld%lld", &y, &z, &p);
        if(cmd == 1) cout << calc1(y, z) << endl;
        else if(cmd == 2) {
            ll Gcd = gcd(y, p);
            if(z % Gcd)
                puts("Orz, I cannot find x!");
            else {
                z %= p;
                y /= Gcd; p /= Gcd; z /= Gcd;
                exgcd(y, p, a, b);
                a = (a * z % p + p) % p;
                printf("%lld\n", a);
            }
        }
        else {
            if(y%p==0) {
                if(z==1) printf("0\n"); else puts("Orz, I cannot find x!");
                continue;
            }
            T.BSGS(y, z, p);
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章