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 ,依然没有发现可行的解,表示无解,退出。

    

 #include<stdio.h>
#include<math.h>
#include<map>
using namespace std;
/*
Baby-Step-Giant-Step: 用于解决形如: A^x = B (mod C) 的问题
<><> 普通的解法只能解决C是素数的情况
首先,令 x= a*m + b ; -- m= Ceil(sqrt(c)); -- Ceil 表示上取整
then 原式等价于 ( (A^a )^m )*(A^b)=B (mod c);
then 那么我们可以枚举 a: 0-m ,记录到 (a,A^a) ,用H 来表示: (A^a)^m ;
那么 原式等价于 H * A^b = B (mod c) ;
then 求出 A^m ; 枚举 a: 0-m ,对于每个值判断有没有 A^b 存在,如果有便退出...
*/
// 这个代码不敢保证正确无误,因为没有找到题来A,不过思路应该是没错
//了解了普通的BSGS,可以去看下面那份代码,注释得比较详细...
#define EPS 0.0000001
typedef long long LL;
LL pow(LL a,LL b,LL c) //快速幂取模
{
LL ans=;
while(b>)
{
if(b&)
{
ans=ans*a%c;
}
a=a*a%c;
b>>=;
}
return ans;
}
void exGcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(b==)
{
d=a;
x=;
y=;
return ;
}
exGcd(b,a%b,d,x,y);
LL t=x;
x=y;
y=t-a/b*y;
}
LL BSGS(LL A,LL B,LL C)
{
map<LL,int> mp;
// LL m=(LL)ceil(sqrt(C)); 据英明神武的某朱说Gcc不支持这个ceil
LL m=(LL)(sqrt(C)+-EPS);
for(int i=m-;i>=;i--)
{
LL h=pow(A,i,C);
mp[h]=i;
}
LL h=pow(A,m,C);
LL d,x,y;
for(int i=;i<m;i++)
{
LL k=pow(h,i,C);
exGcd(k,C,d,x,y);
if(B%d!=) continue;
x=((x*B/d%C)+C)%C;
if(mp.find(x)!=mp.end()) return mp[x]+i*m;
}
return -;
}
int main()
{
LL A,B,C;
while(scanf("%I64d%I64d%I64d",&A,&B,&C)!=EOF)
{
printf("%I64d\n",BSGS(A,B,C));
}
return ;
}

代码

  二.     扩展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,自己找有点浪费时间,也没多大收获...

     

 #include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<conio.h>
#define Mod 40007 //取模的大小,哈希表的大小...
#define Max 100000 //存放的总数
#define EPS 0.000000001//精度
typedef long long LL;
class Hash //手写哈希
{
public:
int hs[Mod]; //哈希值 设定的哈希函数为 原值 % Mod ,所以哈希值有可能是 0 ~ Mod-1
int next[Max]; //链表 存放哈希值相等的一条链,他的大小取决于所有原值的数量
LL S[Max]; //存放原值
int H[Max]; //存放所有哈希值
int sn; //不同原值的数量
int hn; //不同哈希值的数量
Hash() //构造函数: 定义Hash类变量时初始化
{
sn=;
hn=;
for(int i=;i<Mod;i++)
hs[i]=;
}
void clear() //清空函数
{
sn=;
for(int i=;i<hn;i++)
hs[H[i]]=;
hn=;
}
void add(LL s) //加入
{
int ha=abs(s)%Mod; //计算哈希值
if(hs[ha]==) //如果该哈希值还未出现过
{
H[hn++]=ha; //将该哈希值记录起来,同时哈希值数量加 1
}
sn++; //0 表示结尾,所以从1 开始存,原值数量加 1,特别针对 hs数组
S[sn]=s; //将原值记录起来
next[sn]=hs[ha]; //原本原值记录位置
hs[ha]=sn; //最新原值记录位置,如果从0 开始存,就无法判断此时是空还是1个值
//比如:5 和 10 有一样的哈希值 ,并且 5 和 10 先后加入 那么有:
//5 加入: next[1] = 0; hs[5] = 1; hs[5] 是哈希值为5 的头,表示第一个原值在1的位置
//10加入: next[2] = 1; hs[5] = 2; 表示第一个哈希值为5的在2,第二个在1,第三个不存在
}
int find(LL s) //查找
{
int ha=abs(s)%Mod; //计算哈希值
int k=hs[ha]; //头
while(k!=)
{
if(S[k]==s) return k;//找到
k=next[k]; //下一个节点
}
return ; //表示没找到
}
};
int pow(int a,int b,int c) //快速幂取模
{
LL ans=;
a%=c;
while(b>)
{
if(b&)
{
ans=(LL)ans*a%c;
}
b>>=;
a=(LL)a*a%c;
}
return ans;
}
void exGcd(int a,int b,int &d,int &x,int &y) //扩展欧几里得
{
if(b==)
{
d=a;
x=;
y=;
return ;
}
exGcd(b,a%b,d,x,y);
LL t=x;
x=y;
y=t-a/b*y;
}
int Gcd(int a,int b) //最大公约数
{
return b?Gcd(b,a%b):a;
}
/*
对于 A^x = B (mod C) 这个式子,如果无法保证C是素数,就得使用扩展Baby-Step-Giant-Step
首先: 有 A^x = C * k + B ; 然后,我们一步步地消去 A 和 C 的公因子
令 D 从 1 开始, 原式就可以化为 D*A^x = C*k+B;
--> D*A/(A,C)*A^(x-1) = C/(A,C)*k +B/(A,C);
--> ...
--> D*(A/(A,C))^(co))*A^(x-co) = C/((A,C)^co)*k + B/((A,C)^co)
上面的那个循环知道 A、C互质时结束,没进行一次,D便等于 D*A/(A,C);
最后,原式化为 D * A^(x-co) = B (mod C) 如果上面循环中,出现B无法整除的情况就表示无解退出
这样,我们便保证了 D 和 A 都和 C 互质,这时候,便可以利用普通的Baby-Step-Giant-Step解决
同时,要小心的一点就是,因为这样解出来的最小解是肯定大于co的,但如果真实解小于co,就会出
错,所以我们可以事先枚举0 - p 的i,进行特判,这里的p=log(C),因为每次消因子最少也要消去2,
所以最多消log(C)次,co最大也就是这个了。
*/
int BSGS(int A,int B,int C) //扩展 Baby-Step-Giant-Step
{
Hash hp;
for(int i=;i<;i++) //枚举1 - log(C) ,可以稍大一点...
{
if(pow(A,i,C)==B) return i;
}
int tmp,co=;
LL D=,k=;
while((tmp=Gcd(A,C))!=) //消因子循环
{
co++;
if(B%tmp!=) return -;
C/=tmp;
B/=tmp;
D=D*A/tmp%C;
}
hp.clear();
int m=(int)(sqrt((double)C)-EPS); //m=ceil(sqrt(C)),这里的ceil表示上取整
for(int i=;i<m;i++,k=k*A%C)
if(hp.find(k)==) hp.add(k);
int d,x,y;
for(int i=;i<m;i++)
{
exGcd(D,C,d,x,y);
x=((LL)x*B%C+C)%C; //不用进行B是否能整除d的判断,因为D和C永远互质
int w=hp.find(x);
if(w!=) return w-+co+i*m;
D=D*k%C; //D最开始的时候和C互质,同时k也和C互质
}
return -; //找不到
}
int main()
{
int A,B,C;
while(scanf("%d%d%d",&A,&C,&B)!=EOF)
{
if(B>=C)
{
printf("Orz,I can’t find D!\n");
continue;
}
int t=BSGS(A,B,C);
if(t==-)
printf("Orz,I can’t find D!\n");
else printf("%d\n",t);
}
return ;
}

最新文章

  1. 获取 dhcp IP 过程分析 - 每天5分钟玩转 OpenStack(91)
  2. DNS压力测试工具dnsperf简介
  3. html focus事件小学问
  4. TYVJ P1068 STR Label:KMP匹配 不懂
  5. 牧场安排(usaco NOV06.cowfood)
  6. 关于&lt;html&gt;标签里的class= no-js
  7. interactive_timeout
  8. FFT初步学习小结
  9. 在代码中创建Drawable资源
  10. 基于Processing的数据可视化
  11. 华为OJ:查找字符的第一个字符串只出现一次
  12. UIView(包括子类)的几个初始化时执行动作的时机
  13. JavaScript 定义 类
  14. C语言老司机学Python (六)- 多线程
  15. .net core 中间件管道底层剖析
  16. java多线程(7)---Condition
  17. pandas 时间格式转换
  18. C# 服务开发
  19. [转]Cordova Android 返回键拦截(backbutton)和退出(再点击一次跳出)
  20. Rookey.Frame v1.0快速开发平台-整体介绍

热门文章

  1. 在已经安装的nginx上,增加ssl模块
  2. WPF 使用Console.Write打印信息到控制台窗口中
  3. 16.ajax_case03
  4. CCF-201803-3-URL映射(模拟)
  5. 解决y7000笔记本ubuntu18.04下 休眠挂起后唤醒花屏
  6. Spring事务嵌套
  7. sqlalchemy和flask-sqlalchemy的几种分页方法
  8. GDB 命令回顾
  9. 从String.valueOf(null)说起
  10. Java IO(四)——字符流