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;
}