离散对数,giant-step baby-step,拓展giant-step baby-step (poj 2417, hdu 2815)

2015-8-25 修改:离散对数 (a ^ x) % p = 1 时,可以用欧拉定理解决,且效率较高

离散对数,giant-step baby-step,拓展giant-step baby-step (poj 2417, hdu 2815)


普通giant-step baby-step:
poj 2417


题意:
B^L==N(MOD P), 给出P,B,N, 求最小的非负L。

限制:
2 <= P < 2^31 && P为素数; 2 <= B <P; 1 <= N < P

思路:
离散对数,用giant-step baby-step解决。
普通giant-step baby-step过程,要求P为素数:
令s = ceil(sqrt(P)), 则L = b * s + r (0 <= b, r < s),
即B^L = B^(b * s) * B^r。把所有B^r放入hash表中,从小到大枚举b,得到B^(b * s) * B^r = N。
费马小定理a^(m-1)=1(mod m), m为素数, 可得:
1) a^0=1,所以循环节小于等于m,即如果存在解,则最小解x<=m
2) 对于a^s模m的逆元为a^(m-s-1)


/*poj 2417
  题意:
  B^L==N(MOD P), 给出P,B,N, 求最小的非负L。
  限制:
  2 <= P < 2^31 && P为素数; 2 <= B 
  
  
   
   < P
  思路:
  离散对数,用giant-step baby-step解决。

  普通giant-step baby-step过程,要求P为素数:
  令s = ceil(sqrt(P)), 则L = b * s + r (0 <= b, r < s),
  即B^L = B^(b * s) * B^r。把所有B^r放入hash表中,从小到大枚举b,得到N^(b * s) * B^r = n。


  费马小定理a^(m-1)=1(mod m), m为素数, 可得:
  1) a^0=1,所以循环节小于等于m,即如果存在解,则最小解x<=m
  2) 对于a^s模m的逆元为a^(m-s-1)
 */
#include
   
   
    
    
#include
    
    
     
     
#include
     
     
#include
      
      
        #include 
       
         using namespace std; #define LL __int64 const int HASH_MOD=76543; LL key[HASH_MOD], val[HASH_MOD]; int head[HASH_MOD], next[HASH_MOD]; struct Hash{ int tot; void init(){ memset(head, -1, sizeof(head)); tot = 0; } LL insert(LL x, LL y){ int k = x % HASH_MOD; key[tot] = x; val[tot] = y; next[tot] = head[k]; head[k] = tot++; } LL find(LL x){ int k = x % HASH_MOD; for(int i = head[k]; i != -1; i = next[i]) if(key[i] == x) return val[i]; return -1; } }hs; LL a_b_MOD_c(LL a,LL b,LL mod){ LL ret = 1; a %= mod; while(b){ if(b & 1) ret = ret * a % mod; a = a * a % mod; b >>= 1; } return ret; } //求解模方程a^x=b(mod m),n为素数,无解返回-1 //注意:要求0 < a < m; 0 <= b < m; 否则按题意自己转化。 //复杂度O(sqrt(m)) LL log_mod(LL a, LL b, LL m){ hs.init(); LL s = ceil(sqrt(m)); LL cur = 1; for (int i = 0; i < s; ++i){ if(hs.find(cur)==-1) hs.insert(cur,i); //记得先判重,在插入 cur = cur * a % m; } LL v = a_b_MOD_c(a, (m - s - 1 + m) % m, m); for(int i = 0; i < s; ++i){ LL tmp = hs.find(b); if(tmp!=-1) return s * i + tmp; b=b*v%m; } return -1; } int main(){ int p, b, n; while(scanf("%d%d%d", &p, &b, &n) != EOF){ LL ans = log_mod(b,n,p); if(ans == -1) puts("no solution"); else printf("%I64d\n", ans); } return 0; } 
        
      
    
    
   
   
  
  
 

拓展giant-step baby-step:
hdu 2815

题意:
给出K,P,N, 求最小的D使得:K^D == N(MOD P)

限制:
1 <= K,P,N <= 1e9; 注意P不一定是素数。

思路:
离散对数
因为, P不一定是素数, 所以用拓展giant-step baby-step解决。 
拓展giant-step baby-step思路:
当为P合数时,我们就需要把Baby-Step Giant-Step扩展一下。在普通Baby-Step Giant-Step中,由于是素数,那么,所以一定有唯一解的。那么,当为合数时,我们可以这样处理:
对于方程:A^x + Cy = B, 我们拿出若干A出来与C来消去公共因子,使(A, C) = 1为止, 然后可以得到类似这样的式子:a * A^x' + C'y = B', 然后用拓展欧几里得计算结果,其余部分与普通giant-step baby-step相似。 


注意:
假设消因子过程做了cnt次,最后的答案ans为, 如果ans < cnt的话,会有问题,因为由上面过程算出来的答案是最后加上cnt的,所以会有错。所以还要加上一步,从先从 0 到 50 枚举答案,找不到答案,再执行上面的过程。因为P < 1e9, 每次消因子最少消2,所以cnt < 50。 


/*hdu 2815
  题意:
  给出K,P,N, 求最小的D使得:K^D == N(MOD P)
  限制:
  1 <= K,P,N <= 1e9; 注意P不一定是素数。
  思路:
  离散对数
  因为, P不一定是素数, 所以用拓展giant-step baby-step解决。

  拓展giant-step baby-step思路:
  当为P合数时,我们就需要把Baby-Step Giant-Step扩展一下。在普通Baby-Step Giant-Step中,由于是素数,那么,所以一定有唯一解的。那么,当为合数时,我们可以这样处理:
  对于方程:A^x + Cy = B, 我们拿出若干A出来与C来小区公共因子,使(A, C) = 1为止, 然后可以得到类似这样的式子:a * A^x' + C'y = B, 然后用拓展欧几里得计算结果,其余部分与普通giant-step baby-step相似。

  注意:
  假设消因子过程做了cnt次,最后的答案ans为, 如果ans < cnt的话,会有问题,因为由上面过程算出来的答案是最后加上cnt的,所以会有错。所以还要加上一步,从先从 0 到 50 枚举答案,找不到答案,再执行上面的过程。因为P < 1e9, 每次消因子最少消2,所以cnt < 50。
 */
#include 
  
  
   
   
#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
        using namespace std; #define LL __int64 const int HASH_MOD=76543; LL key[HASH_MOD], val[HASH_MOD]; int head[HASH_MOD], next[HASH_MOD]; struct Hash{ int tot; void init(){ memset(head, -1, sizeof(head)); tot = 0; } LL insert(LL x, LL y){ int k = x % HASH_MOD; key[tot] = x; val[tot] = y; next[tot] = head[k]; head[k] = tot++; } LL find(LL x){ int k = x % HASH_MOD; for(int i = head[k]; i != -1; i = next[i]) if(key[i] == x) return val[i]; return -1; } }hs; LL ext_gcd(LL a,LL b,LL &x,LL &y){ if(b==0) { x=1, y=0; return a; } LL ret= ext_gcd(b,a%b,y,x); y-= a/b*x; return ret; } //(A^x)%C=B //注意0 < A < C; 0 <= B < C //a*x+Cy=B LL ext_log_mod(LL A,LL B,LL C){ LL ret = 1; for(int i=0; i<=50; i++){ if(ret == B) return i; ret = ret * A % C; } hs.init(); LL a = 1; LL d, cnt = 0; while((d = __gcd(A,C)) != 1){ if(B % d) return -1; B /= d; C /= d; a = a * (A / d) % C; ++cnt; } LL s = ceil(sqrt(C*1.0)); LL cur = 1; for(int i = 0; i < s; ++i){ if(hs.find(cur)==-1) hs.insert(cur, i); //记得先判重,再插入 cur = cur * A % C; } for(int i = 0; i < s; ++i){ LL x, y; d = ext_gcd(a, C, x, y); x = (x * (B / d) % (C / d) + (C / d)) % (C / d); LL fd = hs.find(x); if(fd != -1) return i * s + fd + cnt; a = a * cur % C; // } return -1; } int main(){ LL A, B, C; while(scanf("%I64d%I64d%I64d", &A, &C, &B) != EOF){ A %= C; //记得这些细节 //B %= C; if(B >= C){ puts("Orz,I can’t find D!"); continue; } LL ans = ext_log_mod(A,B,C); if(ans == -1) puts("Orz,I can’t find D!"); else printf("%I64d\n",ans); } return 0; } 
      
     
     
    
    
   
   
  
  



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值