Baby-Step-Giant-Step 很酷的算法

Baby-Step-Giant-Step

BSGS算法用于解决形如:      A  ^  x  ≡  B  (  mod  C  ) 的问题。

   学这个算法前需要具备以下知识:快速幂取模、扩展欧几里得、同余知识、哈希表(也可以用map,不过更耗时).. 

  一.     普通的Baby-Step-Giant-Step

    对于普通的BSGS,使用时有个限制条件就是:C只能是一个素数。当C 不是素数的时候,便要用到扩展BSGS,我们先来看普通的…

    做法:首先令 x = a * m + b ;  m = ceil ( sqrt ( C ) ) ;  [ceil: 上取整] 0 <= a < m , 0 <= b < m ;

    Then 原式等价于 ( ( A ^ a ) ^ m ) * A ^ b ≡ B (mod C );

    Then 我们枚举a ,从0 到 m ,对每个a ,算出 A^a ,放到哈希表或map里(用哈希更高效…这里先用map) ,如 h=A^a; mp[h]=a;然后我们用 D 来表示 ( ( A ^ a ) ^ m ) ,用Y来表示 A^B 原式便可化为 : D * Y ≡ B (mod C);

    Then 我们再次枚举 a 从 0 到 m ,可以逐个算出 D 的值,在有了D值得基础上,运用扩展欧几里得可以算出 Y ,现在查找原本记录的表,查看这个Y 值是否存在,如果存在便可返回 a^m + b ;

    如果找完0 到 m ,依然没有发现可行的解,表示无解,退出。

    

 1 #include<stdio.h>
 2 #include<math.h>
 3 #include<map>
 4 using namespace std;
 5 /*
 6   Baby-Step-Giant-Step:   用于解决形如: A^x = B (mod C) 的问题
 7   <><> 普通的解法只能解决C是素数的情况
 8   首先,令 x= a*m + b ; -- m= Ceil(sqrt(c));  --  Ceil 表示上取整
 9   then 原式等价于 ( (A^a )^m )*(A^b)=B (mod c);
10   then 那么我们可以枚举 a: 0-m ,记录到 (a,A^a) ,用H 来表示: (A^a)^m ;
11   那么 原式等价于 H * A^b = B (mod c) ;
12   then 求出 A^m ; 枚举 a: 0-m ,对于每个值判断有没有 A^b 存在,如果有便退出...
13 */
14 // 这个代码不敢保证正确无误,因为没有找到题来A,不过思路应该是没错
15 //了解了普通的BSGS,可以去看下面那份代码,注释得比较详细...
16 #define EPS 0.0000001
17 typedef long long LL;
18 LL pow(LL a,LL b,LL c)    //快速幂取模
19 {
20   LL ans=1;
21   while(b>0)
22   {
23     if(b&1)
24     {
25       ans=ans*a%c;
26     }
27     a=a*a%c;
28     b>>=1;
29   }
30   return ans;
31 }
32 void exGcd(LL a,LL b,LL &d,LL &x,LL &y)
33 {
34   if(b==0)
35   {
36     d=a;
37     x=1;
38     y=0;
39     return ;
40   }
41   exGcd(b,a%b,d,x,y);
42   LL t=x;
43   x=y;
44   y=t-a/b*y;
45 }
46 LL BSGS(LL A,LL B,LL C)
47 {
48   map<LL,int> mp;
49   // LL m=(LL)ceil(sqrt(C));  据英明神武的某朱说Gcc不支持这个ceil
50   LL m=(LL)(sqrt(C)+1-EPS);
51   for(int i=m-1;i>=0;i--)
52   {
53     LL h=pow(A,i,C);
54     mp[h]=i;
55   }
56   LL h=pow(A,m,C);
57   LL d,x,y;
58   for(int i=0;i<m;i++)
59   {
60     LL k=pow(h,i,C);
61     exGcd(k,C,d,x,y);
62     if(B%d!=0) continue;
63     x=((x*B/d%C)+C)%C;
64     if(mp.find(x)!=mp.end()) return mp[x]+i*m;
65   }
66   return -1;
67 }
68 int main()
69 {
70   LL A,B,C;
71   while(scanf("%I64d%I64d%I64d",&A,&B,&C)!=EOF)
72   {
73     printf("%I64d\n",BSGS(A,B,C));
74   }
75   return 0;
76 }

代码

  二.     扩展Baby-Step-Giant-Step

    对于C不是素数的情况,不能照搬上面的做法,但也大同小异。

      下面我以hdu的模板题来介绍一下:

      题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2815

      题意: 给出3个数,A,C,B 表示 A^x ≡ B (mod C ) ,让你求最小的x ; 标准的模板题了,不过题目有些小坑,可以先看下discuss,自己找有点浪费时间,也没多大收获...

     

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<math.h>
  4 #include<conio.h>
  5 #define Mod 40007       //取模的大小,哈希表的大小...
  6 #define Max 100000     //存放的总数
  7 #define EPS 0.000000001//精度
  8 typedef long long LL;
  9 class Hash             //手写哈希
 10 {
 11   public:
 12     int hs[Mod];       //哈希值  设定的哈希函数为 原值 % Mod ,所以哈希值有可能是 0 ~ Mod-1
 13     int next[Max];     //链表    存放哈希值相等的一条链,他的大小取决于所有原值的数量
 14     LL S[Max];         //存放原值
 15     int H[Max];        //存放所有哈希值
 16     int sn;            //不同原值的数量
 17     int hn;            //不同哈希值的数量
 18     Hash()             //构造函数: 定义Hash类变量时初始化
 19     {
 20       sn=0;
 21       hn=0;
 22       for(int i=0;i<Mod;i++)
 23         hs[i]=0;
 24     }
 25     void clear()       //清空函数
 26     {
 27       sn=0;
 28       for(int i=0;i<hn;i++)
 29         hs[H[i]]=0;
 30       hn=0;
 31     }
 32     void add(LL s)           //加入
 33     {
 34       int ha=abs(s)%Mod;     //计算哈希值
 35       if(hs[ha]==0)          //如果该哈希值还未出现过
 36       {
 37         H[hn++]=ha;          //将该哈希值记录起来,同时哈希值数量加 1
 38       }
 39       sn++;                  //0 表示结尾,所以从1 开始存,原值数量加 1,特别针对 hs数组
 40       S[sn]=s;               //将原值记录起来
 41       next[sn]=hs[ha];       //原本原值记录位置
 42       hs[ha]=sn;             //最新原值记录位置,如果从0 开始存,就无法判断此时是空还是1个值
 43       //比如:5 和 10 有一样的哈希值 ,并且 5 和 10 先后加入 那么有:
 44       //5 加入: next[1] = 0; hs[5] = 1; hs[5] 是哈希值为5 的头,表示第一个原值在1的位置
 45       //10加入: next[2] = 1; hs[5] = 2; 表示第一个哈希值为5的在2,第二个在1,第三个不存在
 46     }
 47     int find(LL s)           //查找
 48     {
 49       int ha=abs(s)%Mod;     //计算哈希值
 50       int k=hs[ha];          //头
 51       while(k!=0)
 52       {
 53         if(S[k]==s) return k;//找到
 54         k=next[k];           //下一个节点
 55       }
 56       return 0;              //表示没找到
 57     }
 58 };
 59 int pow(int a,int b,int c)   //快速幂取模
 60 {
 61   LL ans=1;
 62   a%=c;
 63   while(b>0)
 64   {
 65     if(b&1)
 66     {
 67       ans=(LL)ans*a%c;
 68     }
 69     b>>=1;
 70     a=(LL)a*a%c;
 71   }
 72   return ans;
 73 }
 74 void exGcd(int a,int b,int &d,int &x,int &y)     //扩展欧几里得
 75 {
 76   if(b==0)
 77   {
 78     d=a;
 79     x=1;
 80     y=0;
 81     return ;
 82   }
 83   exGcd(b,a%b,d,x,y);
 84   LL t=x;
 85   x=y;
 86   y=t-a/b*y;
 87 }
 88 int Gcd(int a,int b)            //最大公约数
 89 {
 90   return b?Gcd(b,a%b):a;
 91 }
 92 /*
 93   对于 A^x = B (mod C) 这个式子,如果无法保证C是素数,就得使用扩展Baby-Step-Giant-Step
 94   首先: 有 A^x = C * k + B ; 然后,我们一步步地消去 A 和 C 的公因子
 95   令 D 从 1 开始, 原式就可以化为 D*A^x = C*k+B;
 96                               --> D*A/(A,C)*A^(x-1) = C/(A,C)*k +B/(A,C);
 97                               --> ...
 98                               --> D*(A/(A,C))^(co))*A^(x-co) = C/((A,C)^co)*k + B/((A,C)^co)
 99   上面的那个循环知道 A、C互质时结束,没进行一次,D便等于 D*A/(A,C);
100   最后,原式化为 D * A^(x-co) = B (mod C) 如果上面循环中,出现B无法整除的情况就表示无解退出
101   这样,我们便保证了 D 和 A 都和 C 互质,这时候,便可以利用普通的Baby-Step-Giant-Step解决
102   同时,要小心的一点就是,因为这样解出来的最小解是肯定大于co的,但如果真实解小于co,就会出
103   错,所以我们可以事先枚举0 - p 的i,进行特判,这里的p=log(C),因为每次消因子最少也要消去2,
104   所以最多消log(C)次,co最大也就是这个了。
105 */
106 int BSGS(int A,int B,int C)     //扩展 Baby-Step-Giant-Step
107 {
108   Hash hp;
109   for(int i=0;i<50;i++)         //枚举1 - log(C) ,可以稍大一点...
110   {
111     if(pow(A,i,C)==B) return i;
112   }
113   int tmp,co=0;
114   LL D=1,k=1;
115   while((tmp=Gcd(A,C))!=1)      //消因子循环
116   {
117     co++;
118     if(B%tmp!=0) return -1;
119     C/=tmp;
120     B/=tmp;
121     D=D*A/tmp%C;
122   }
123   hp.clear();
124   int m=(int)(sqrt((double)C)-EPS);   //m=ceil(sqrt(C)),这里的ceil表示上取整
125   for(int i=0;i<m;i++,k=k*A%C)
126     if(hp.find(k)==0) hp.add(k);
127   int d,x,y;
128   for(int i=0;i<m;i++)
129   {
130     exGcd(D,C,d,x,y);
131     x=((LL)x*B%C+C)%C;                //不用进行B是否能整除d的判断,因为D和C永远互质
132     int w=hp.find(x);
133     if(w!=0) return w-1+co+i*m;
134     D=D*k%C;                          //D最开始的时候和C互质,同时k也和C互质
135   }
136   return -1;                          //找不到
137 }
138 int main()
139 {
140   int A,B,C;
141   while(scanf("%d%d%d",&A,&C,&B)!=EOF)
142   {
143     if(B>=C)
144     {
145       printf("Orz,I can’t find D!\n");
146       continue;
147     }
148     int t=BSGS(A,B,C);
149     if(t==-1)
150       printf("Orz,I can’t find D!\n");
151     else printf("%d\n",t);
152   }
153   return 0;
154 }
时间: 2024-09-05 15:51:02

Baby-Step-Giant-Step 很酷的算法的相关文章

HDU 2815 扩展baby step giant step 算法

题目大意就是求 a^x = b(mod c) 中的x 用一般的baby step giant step 算法会超时 这里参考的是http://hi.baidu.com/aekdycoin/item/236937318413c680c2cf29d4 map平衡树查找值 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <cmath> 5 #include <

HDU 2815 Mod Tree 离散对数 扩展Baby Step Giant Step算法

链接:http://acm.hdu.edu.cn/showproblem.php?pid=2815 题意: 思路:与上题不同,这道题不要求m是素数,是利用扩展Baby Step Giant Step算法求离散对数. 以下转载自:AekdyCoin [扩展Baby Step Giant Step] [问题模型] 求解 A^x = B (mod C) 中 0 <= x < C 的解,C 无限制(当然大小有限制--) [写在前面] 这个问题比较麻烦,目前网络上流传许多版本的做法,不过大部分已近被证明

数论之高次同余方程(Baby Step Giant Step + 拓展BSGS)

什么叫高次同余方程?说白了就是解决这样一个问题: A^x=B(mod C),求最小的x值. baby step giant step算法 题目条件:C是素数(事实上,A与C互质就可以.为什么?在BSGS算法中是要求a^m在%c条件下的逆元的,如果a.c不互质根本就没有逆元.) 如果x有解,那么0<=x<C,为什么? 我们可以回忆一下欧拉定理: 对于c是素数的情况,φ(c)=c-1 那么既然我们知道a^0=1,a^φ(c)=1(在%c的条件下).那么0~φ(c)必定是一个循环节(不一定是最小的)

POJ 2417 Discrete Logging ( Baby step giant step )

Discrete Logging Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 3696   Accepted: 1727 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, b

【poj2417】baby step giant step

最近在学习数论,然而发现之前学的baby step giant step又忘了,于是去翻了翻以前的代码,又复习了一下. 觉得总是忘记是因为没有彻底理解啊. 注意baby step giant step只能用在b和p互质的情况下,因为只有b和p互质的情况下,b才有mod p下的逆元.(下面要用到逆元) 当b和p不互质,就要处理一下.现在就正在做这么一题,方法以后再写. 求a^(-m)就用到了求逆元了,那么如何求逆元呢?我学了两种方法: ·1:欧拉定理:当a和n互质,a^φ ( n) ≡ 1(mod

HDU 2815 Mod Tree (扩展 Baby Step Giant Step )

Mod Tree Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 96 Accepted Submission(s): 38   Problem Description   The picture indicates a tree, every node has 2 children.  The depth of the nodes whos

扩展baby step giant step模版

#include <cstdio> #include <cstring> #include <cmath> #include <map> using namespace std; typedef __int64 LL; LL gcd(LL a, LL b) { return b ? gcd(b, a%b) : a; } LL pow_mod(LL a, LL p, LL n) { LL ans = 1; while(p) { if(p&1) { an

Baby Step Gaint Step

给定同余式,求它在内的所有解,其中总是素数. 分析:解本同余式的步骤如下 (1)求模的一个原根 (2)利用Baby Step Giant Step求出一个,使得,因为为素数,所以有唯一解. (3)设,这样就有,其中,那么得到. (4)求出所有的,可以知道一共有个解,我们求出所有的,然后排个序即可. O(sqrt(n))的时间复杂度 BSGS如下(前向星版本) const maxn=200001; type node=record data,next,id:longint; end; type L

Step by Step 改进朴素贝叶斯算法

引言 如果你对naive bayes认识还处于初级阶段,只了解基本的原理和假设,还没有实现过产品级的代码,那么这篇文章能够帮助你一步步对原始的朴素贝叶斯算法进行改进.在这个过程中你将会看到朴素贝叶斯假设的一些不合理处以及局限性,从而了解为什么这些假设在简化你的算法的同时,使最终分类结果变得糟糕,并针对这些问题提出了改进的方法. 朴素贝叶斯(Naive Bayes) 出处: <机器学习>(Machine Learning by Tom M.Mitchell) 符号和术语 假设待分类的实例 X 可

bsgs(Baby Steps Giant Steps)算法

BSGS算法(Baby Steps Giant Steps算法,大步小步算法,北上广深算法,拔山盖世算法) 适用问题 对于式子: $$x^y=z(mod_p)$$ 已知x,z,p,p为质数: 求解一个最小非负整数y: 存在一个y,属于[0,p-2](费马小定理) 于是有了一个笨拙的方法,枚举y 枚举y,期望效率:O(P) 寻求一种优化: 对式子变型: 设:$$y=i\sqrt{p}-j$$ 则$$x^{i\sqrt{p}-j}=z(mod_p)$$ ——这个变型的用意在于把y拆开 枚举y,变成枚