zoukankan      html  css  js  c++  java
  • baby_step giant_step hdu 2815 mod tree pku 3243 Clever Y

    原创帖!转载请注明作者 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;
    }

     

     
  • 相关阅读:
    开始学习Angular Mobile UI
    .Net程序员飞扬有用的85个工具
    数据库查询效率,百万数据测试
    SQL Join的一些总结
    MODRD 指令 读取地址是哪儿来的
    如何在不同编程语言中获取现在的Unix时间戳(Unix timestamp)?
    easyui datagrid 的分页刷新按钮
    HttpRequest Get Post,WebClient Get
    GetWindowThreadProcessId用法(转)
    EASYUI DATAGRID 多列复选框CheckBox
  • 原文地址:https://www.cnblogs.com/xiaoxian1369/p/2128776.html
Copyright © 2011-2022 走看看