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;
}
posted @ 2016-09-02 19:26  pbvrvnq  阅读(173)  评论(0编辑  收藏  举报