原创帖!转载请注明作者 AekdyCoin !
【普通Baby Step Giant Step】
【问题模型】
求解
A^x = B (mod C) 中 0 <= x < C 的解,C 为素数
【思路】
我们可以做一个等价
x = i * m + j ( 0 <= i < m, 0 <=j < m) m = Ceil ( sqrt( C) )
而这么分解的目的无非是为了转化为:
(A^i)^m * A^j = B ( mod C)
之后做少许暴力的工作就可以解决问题:
(1) for i = 0 -> m, 插入Hash (i, A^i mod C)
(2) 枚举 i ,对于每一个枚举到的i,令 AA = (A^m)^i mod C
我们有
AA * A^j = B (mod C)
显然AA,B,C均已知,而由于C为素数,那么(AA,C)无条件为1
于是对于这个模方程解的个数唯一(可以利用扩展欧几里得或 欧拉定理来求解)
那么对于得到的唯一解X,在Hash表中寻找,如果找到,则返回 i * m + j
注意:由于i从小到大的枚举,而Hash表中存在的j必然是对于某个剩余系内的元素X 是最小的(就是指标)
所以显然此时就可以得到最小解
如果需要得到 x > 0的解,那么只需要在上面的步骤中判断 当 i * m + j > 0 的时候才返回
到目前为止,以上的算法都不存在争议,大家实现的代码均相差不大。可见当C为素数的时候,此类离散对数的问题可以变得十分容易实现。
【扩展Baby Step Giant Step】
【问题模型】
求解
A^x = B (mod C) 中 0 <= x < C 的解,C 无限制(当然大小有限制……)
【写在前面】
这个问题比较麻烦,目前网络上流传许多版本的做法,不过大部分已近被证明是完全错误的!
这里就不再累述这些做法,下面是我的做法(有问题欢迎提出)
下面先给出算法框架,稍后给出详细证明:
(0) for i = 0 -> 50 if(A^i mod C == B) return i O(50)
(1) d<- 0 D<- 1 mod C
while((tmp=gcd(A,C))!=1)
{
if(B%tmp)return -1; // 无解!
++d;
C/=tmp;
B/=tmp;
D=D*A/tmp%C;
}
(2) m = Ceil ( sqrt(C) ) //Ceil是必要的 O(1)
(3) for i = 0 -> m 插入Hash表(i, A^i mod C) O( m)
(4) K=pow_mod(A,m,C)
for i = 0 -> m
解 D * X = B (mod C) 的唯一解 (如果存在解,必然唯一!)
之后Hash表中查询,若查到(假设是 j),则 return i * m + j + d
否则
D=D*K%C,继续循环
(5) 无条件返回 -1 ;//无解!
下面是证明:
推论1:
A^x = B(mod C)
等价为
A^a * A^b = B ( mod C) (a+b) == x a,b >= 0
证明:
A^x = K * C + B (模的定义)
A^a * A^b = K*C + B( a,b >=0, a + b == x)
所以有
A^a * A^b = B(mod C)
推论 2:
令 AA * A^b = B(mod C)
那么解存在的必要条件为: 可以得到至少一个可行解 A^b = X (mod C)
使上式成立
推论3
AA * A^b = B(mod C)
中解的个数为 (AA,C)
由推论3 不难想到对原始Baby Step Giant Step的改进
For I = 0 -> m
For any solution that AA * X = B (mod C)
If find X
Return I * m + j
而根据推论3,以上算法的复杂度实际在 (AA,C)很大的时候会退化到几乎O(C)
归结原因,是因为(AA,C)过大,而就是(A,C)过大
于是我们需要找到一中做法,可以将(A,C)减少,并不影响解
下面介绍一种“消因子”的做法
一开始D = 1 mod C
进行若干论的消因子,对于每次消因子
令 G = (A,C[i]) // C[i]表示经过i轮消因子以后的C的值
如果不存在 G | B[i] //B[i]表示经过i轮消因子以后的B的值
直接返回无解
否则
B[i+1] = B[i] / G
C[i+1] = C[i] / G
D = D * A / G
具体实现只需要用若干变量,细节参考代码
假设我们消了a'轮(假设最后得到的B,C分别为B',C')
那么有
D * A^b = B' (mod C')
于是可以得到算法
for i = 0 -> m
解 ( D* (A^m) ^i ) * X = B'(mod C')
由于 ( D* (A^m) ^i , C') = 1 (想想为什么?)
于是我们可以得到唯一解
之后的做法就是对于这个唯一解在Hash中查找
这样我们可以得到b的值,那么最小解就是a' + b !!
现在问题大约已近解决了,可是细心看来,其实还是有BUG的,那就是
对于
A^x = B(mod C)
如果x的最小解< a',那么会出错
而考虑到每次消因子最小消 2
故a'最大值为log(C)
于是我们可以暴力枚举0->log(C)的解,若得到了一个解,直接返回
否则必然有 解x > log(C)
PS.以上算法基于Hash 表,如果使用map等平衡树维护,那么复杂度会更大
hdu 2815
#include<stdio.h> #include<math.h> #include<stdlib.h> #define LL long long #define nmax 5000005 typedef struct node { int num, ii; } node; node Node[nmax]; int x, y; int cmp(const void *n, const void *m) { node *a = (node *) n; node *b = (node *) m; if (a->num == b->num) { if (a->ii > b->ii) { return 1; } return -1; } if (a->num > b->num) { return 1; } return -1; } int gcd(int a, int b) { int te; if (a < b) { te = a, a = b, b = te; } if (b == 0) { return a; } return gcd(b, a % b); } int ext_gcd(int a, int b) { if (b == 0) { x = 1, y = 0; return a; } int d = ext_gcd(b, a % b); int tx = x; x = y, y = tx - a / b * y; return d; } int inval(int a, int b, int n) { int e; ext_gcd(a, n); e = (LL) x * b % n; return e < 0 ? e + n : e; } int modular_exp(int a, int b, int c) { LL ret = 1 % c, te = a % c; while (b) { if (b & 1) { ret = ret * te % c; } te = te * te % c; b >>= 1; } return (int) ret; } int bsearchn(int x, int n) { int left, mid, right; left = 0, right = n + 1; while (left <= right) { mid = (left + right) / 2; if (Node[mid].num == x) { return Node[mid].ii; } else if (Node[mid].num > x) { right = mid - 1; } else { left = mid + 1; } } return -1; } int baby_step_giant_step(int a, int b, int c) { int i, j, d, tep, m, K, tem, k; LL D, buf; for (i = 0, buf = 1 % c, D = buf; i < 100; i++, buf = buf * a % c) { if (buf == b) { return i; } } d = 0; while ((tep = gcd(a, c)) != 1) { if (b % tep) { return -1; } ++d; c /= tep, b /= tep; D = D * a / tep % c; } m = (int) (ceil(sqrt((double) c))); for (i = 0, buf = 1 % c; i <= m; i++, buf = buf * a % c) { Node[i].num = (int) buf; Node[i].ii = i; } qsort(Node, m + 1, sizeof(Node[0]), cmp); for (i = 0, j = 0, k = -1; i <= m; i++) { if (k != Node[i].num) { Node[j++] = Node[i]; k = Node[i].num; } } m = j; K = modular_exp(a, m, c); for (i = 0; i <= m; i++, D = D * K % c) { tem = inval((int) D, b, c); if (tem >= 0) { if ((j = bsearchn(tem, m)) != -1) { return i * m + j + d; } } } return -1; } int main() { #ifndef ONLINE_JUDGE freopen("t.txt", "r", stdin); #endif int k, p, n, res; while (scanf("%d %d %d", &k, &p, &n) != EOF) { if (n >= p) { puts("Orz,I can’t find D!"); continue; } res = baby_step_giant_step(k, n, p); if (res == -1) { puts("Orz,I can’t find D!"); continue; } printf("%d\n", res); } return 0; }
最新版~!
/*
a^x=b (mod c)
*/
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#define LL long long
#define nnum 100
#define nmax 31625
typedef struct num {
int ii, value;
} num;
num Num[nmax];
int x, y;
int cmp(const void *a, const void *b) {
num n = *(num *) a;
num m = *(num *) b;
return n.value - m.value;
}
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
void extend_gcd(int a, int b) {
int xx;
if (b == 0) {
x = 1, y = 0;
return;
}
extend_gcd(b, a % b);
xx = x;
x = y, y = xx - a / b * y;
}
/*
ax=b (mod n)
*/
int inval(int a, int b, int n) {
LL res;
extend_gcd(a, n);
res = (LL) x;
res = res * b;
res = (res % n + n) % n;
return (int) res;
}
int bfindNum(int key, int n) {
int left, right, mid;
left = 0, right = n;
while (left <= right) {
mid = (left + right) >> 1;
if (Num[mid].value == key) {
return Num[mid].ii;
} else if (Num[mid].value > key) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
void baby_step_giant_step(int a, int b, int c) {
int i, j, te, d, cd, aa, ttemp;
LL temp, tem;
if (b >= c) {
puts("Orz,I can’t find D!");
return;
}
for (i = 0, temp = 1 % c, tem = temp; i < nnum; i++, temp = temp * a % c) {
if (temp == b) {
printf("%d\n", i);
return;
}
}
cd = 0;
while ((d = gcd(a, c)) != 1) {
if (b % d) {
puts("Orz,I can’t find D!");
return;
}
cd++;
c /= d, b /= d;
tem = tem * a / d % c;
}
te = (int) (sqrt(c * 1.0) + 0.5);
for (i = 0, temp = 1 % c; i <= te; i++, temp = temp * a % c) {
Num[i].ii = i;
Num[i].value = (int) temp;
}
aa = Num[te].value;
qsort(Num, te + 1, sizeof(Num[0]), cmp);
for (i = 0; i <= te; i++, tem = tem * aa % c) {
ttemp = inval(tem, b, c);
if (ttemp >= 0) {
j = bfindNum(ttemp, te + 1);
if (j != -1) {
printf("%d\n", i * te + j + cd);
return;
}
}
}
puts("Orz,I can’t find D!");
}
void solve(int a, int b, int c) {
baby_step_giant_step(a, b, c);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
#endif
int k, p, n;
while (~scanf("%d %d %d", &k, &p, &n)) {
solve(k, n, p);
}
return 0;
}
pku 3243
/*
a^x=b (mod c)
*/
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#define LL long long
#define nnum 100
#define nmax 31625
typedef struct num {
int ii, value;
} num;
num Num[nmax];
int x, y;
int cmp(const void *a, const void *b) {
num n = *(num *) a;
num m = *(num *) b;
return n.value - m.value;
}
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
void extend_gcd(int a, int b) {
int xx;
if (b == 0) {
x = 1, y = 0;
return;
}
extend_gcd(b, a % b);
xx = x;
x = y, y = xx - a / b * y;
}
/*
ax=b (mod n)
*/
int inval(int a, int b, int n) {
LL res;
extend_gcd(a, n);
res = (LL) x;
res = res * b;
res = (res % n + n) % n;
return (int) res;
}
int bfindNum(int key, int n) {
int left, right, mid;
left = 0, right = n;
while (left <= right) {
mid = (left + right) >> 1;
if (Num[mid].value == key) {
return Num[mid].ii;
} else if (Num[mid].value > key) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
void baby_step_giant_step(int a, int b, int c) {
int i, j, te, d, cd, aa, ttemp;
LL temp, tem;
for (i = 0, temp = 1 % c, tem = temp; i < nnum; i++, temp = temp * a % c) {
if (temp == b) {
printf("%d\n", i);
return;
}
}
cd = 0;
while ((d = gcd(a, c)) != 1) {
if (b % d) {
puts("No Solution");
return;
}
cd++;
c /= d, b /= d;
tem = tem * a / d % c;
}
te = (int) (sqrt(c * 1.0) + 0.5);
for (i = 0, temp = 1 % c; i <= te; i++, temp = temp * a % c) {
Num[i].ii = i;
Num[i].value = (int) temp;
}
aa = Num[te].value;
qsort(Num, te + 1, sizeof(Num[0]), cmp);
for (i = 0; i <= te; i++, tem = tem * aa % c) {
ttemp = inval(tem, b, c);
if (ttemp >= 0) {
j = bfindNum(ttemp, te + 1);
if (j != -1) {
printf("%d\n", i * te + j + cd);
return;
}
}
}
puts("No Solution");
}
void solve(int a, int b, int c) {
baby_step_giant_step(a, b, c);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
#endif
int x, z, k;
while (~scanf("%d %d %d", &x, &z, &k) && (x || z || k)) {
solve(x, k % z, z);
}
return 0;
}