POJ-2417 Discrete Logging (BSGS算法,离散对数)

Discrete Logging
Time Limit: 5000MS Memory Limit: 65536K
Description

Given a prime P, 2 <= P < 231, an integer B, 2 <= B < P, and an integer N, 1 <= N < P, compute the discrete logarithm of N, base B, modulo P. That is, find an integer L such that
B^L == N (mod P)
Input

Read several lines of input, each containing P,B,N separated by a space.
Output

For each line print the logarithm on a separate line. If there are several, print the smallest; if there is none, print “no solution”.
Sample Input

5 2 1
5 2 2
5 2 3
5 2 4
5 3 1
5 3 2
5 3 3
5 3 4
5 4 1
5 4 2
5 4 3
5 4 4
12345701 2 1111111
1111111121 65537 1111111111
Sample Output

0
1
3
2
0
3
1
2
0
no solution
no solution
1
9584351
462803587
Hint

The solution to this problem requires a well known result in number theory that is probably expected of you for Putnam but not ACM competitions. It is Fermat’s theorem that states
B(P-1) == 1 (mod P)

for any prime P and some other (fairly rare) numbers known as base-B pseudoprimes. A rarer subset of the base-B pseudoprimes, known as Carmichael numbers, are pseudoprimes for every base between 2 and P-1. A corollary to Fermat’s theorem is that for any m
B(-m) == B(P-1-m) (mod P) .

题意:对于A^x = B (mod C) 给出A,B,C,求出x,运用bsgs算法。

令x = i*m+j其中(m=ceil(sqrt(C))),带入原式得到,A^(i*m+j) = B (mod C) ,

移项得到 A^j = B / A^(mi) (mod C)

首先将循环j (0到m),将A^j的每一项存入hash表中,

然后令D = A^m,原式变为A^j = B / D^i (mod C)

对于每个B / D^i mod C,查找是否在hash表中,如果存在,此时的 x = i*m+j 就是解

求B/D^i 用到 扩展欧几里德算法 exgcd(D,C,x,y)

将D带入进去就可以得到 D = A^m 关于 C 的逆元 x ,即 inv(D) = x

则此时B / D^i (mod C) = B * inv(D) (mod C) = B * x (mod C)

求逆元的两种方法:
一:费马小定理
费马小定理: a^(p-1) ≡1 (mod p)

两边同除以a:a^(p-2) ≡1/a (mod p) 即为 a^(p-2) ≡inv(a) (mod p)

所以inv(a) = a^(p-2) (mod p)

使用 快速幂取模 即可求出 inv(a) = fastpow( a , p - 2 , p )

二.扩展欧几里德算法
对于a*x + b*y = 1 ,如果ab互质,有解

此式移项得:a*x = 1 -b*y ,对等号两边模b

得到 a*x % b = 1 % b,即a*x = 1 (mod b)

此时解出的x 就是 a关于b的逆元

题解:

#include
#include
#include
#include
#include
#include
#define LL __int64
using namespace std;

class hash {
public:
    hash() {
        memset(a, 0xff, sizeof(a));//初始所有表中值为-1
    }
    int locate(LL x) {
        LL l = x%MOD;
        while (a[l] != x&&a[l] != -1) //循环到已经存了x,或者为空
            l = (l + 1) % MOD;
        return l;
    }
    //插入每个A^j值对应的i
    //x代表A^j,i代表i
    void insert(LL x, LL i) {
        LL l = locate(x);
        if (a[l] == -1) {//如果空就装入,如果已经存了不做任何操作
            a[l] = x;
            v[l] = i;
        }
    }
    LL get(LL x) {
        LL l = locate(x);
        return a[l] == x ? v[l] : -1;//存过就返回数据,否则返回-1
    }
    void clear() {
        memset(a, 0xff, sizeof(a));
    }

private:
    static const LL MOD = 1000007;
    LL a[MOD + 100], v[MOD + 100];
}s;

LL exgcd(LL a, LL b, LL &x, LL &y) {//拓展gcd求逆元
    LL t, ret;
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    ret = exgcd(b, a%b, x, y);
    t = x, x = y, y = t - a / b*y;
    return ret;
}

//A^x=B(modC)  =》 A^j = B*A^(-m*i)(mod C) 
int main() {
    LL  C, A, B, m, t, D, ans, x, y;
    while (scanf("%lld%lld%lld", &C, &A, &B) != EOF) {
        s.clear();
        m = ceil(sqrt((double)C));
        t = 1;
        for (int j = 0; j < m; i++) {//hush储存A^j
            s.insert(t, i);
            t = t*A%C; //t=A^j
        }
        //经过m次后,t=A^m
        D = 1;
        ans = -1;
        for (int i = 0; i < m; i++) {
            exgcd(D, C, x, y);//传入D =A^mi,得到x=A^(-i*m)
            x = ((x*B) % C + C) % C;//B*x=B*D^(-i*m)
            y = s.get(x); //取出j
            if (y != -1) {
                ans = i*m + y; //x = j+m*i
                break;
            }
            D = (D*t) % C;//D=t^i,(t=A^m) =》 D=A^mi
        }
        if(ans == -1) printf("no solution\n");
        else printf("%lld\n", ans);
    }
    return 0;
}

你可能感兴趣的:(ACM)