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 ,依然没有发现可行的解,表示无解,退出。
#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 ;
}
Baby-Step-Giant-Step 很酷的算法的更多相关文章
- POJ 3243 Clever Y (求解高次同余方程A^x=B(mod C) Baby Step Giant Step算法)
不理解Baby Step Giant Step算法,请戳: http://www.cnblogs.com/chenxiwenruo/p/3554885.html #include <iostre ...
- 解高次同余方程 (A^x=B(mod C),0<=x<C)Baby Step Giant Step算法
先给出我所参考的两个链接: http://hi.baidu.com/aekdycoin/item/236937318413c680c2cf29d4 (AC神,数论帝 扩展Baby Step Gian ...
- HDU 2815 Mod Tree 离散对数 扩张Baby Step Giant Step算法
联系:http://acm.hdu.edu.cn/showproblem.php?pid=2815 意甲冠军: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQ ...
- 『高次同余方程 Baby Step Giant Step算法』
高次同余方程 一般来说,高次同余方程分\(a^x \equiv b(mod\ p)\)和\(x^a \equiv b(mod\ p)\)两种,其中后者的难度较大,本片博客仅将介绍第一类方程的解决方法. ...
- HDU 2815 扩展baby step giant step 算法
题目大意就是求 a^x = b(mod c) 中的x 用一般的baby step giant step 算法会超时 这里参考的是http://hi.baidu.com/aekdycoin/item/2 ...
- 【学习笔记】Baby Step Giant Step算法及其扩展
1. 引入 Baby Step Giant Step算法(简称BSGS),用于求解形如\(a^x\equiv b\pmod p\)(\(a,b,p\in \mathbb{N}\))的同余方程,即著名的 ...
- 数论之高次同余方程(Baby Step Giant Step + 拓展BSGS)
什么叫高次同余方程?说白了就是解决这样一个问题: A^x=B(mod C),求最小的x值. baby step giant step算法 题目条件:C是素数(事实上,A与C互质就可以.为什么?在BSG ...
- [置顶] hdu2815 扩展Baby step,Giant step入门
题意:求满足a^x=b(mod n)的最小的整数x. 分析:很多地方写到n是素数的时候可以用Baby step,Giant step, 其实研究过Baby step,Giant step算法以后,你会 ...
- POJ 2417 Discrete Logging ( Baby step giant step )
Discrete Logging Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 3696 Accepted: 1727 ...
- 【POJ2417】baby step giant step
最近在学习数论,然而发现之前学的baby step giant step又忘了,于是去翻了翻以前的代码,又复习了一下. 觉得总是忘记是因为没有彻底理解啊. 注意baby step giant step ...
随机推荐
- spark on yarn 集群部署
概述 hadoop2.7.1 spark 1.5.1 192.168.31.62 resourcemanager, namenode, master 192.168.31.63 nodeman ...
- JSP内置对象概述
JSP内置对象预先定义了九个这个的对象: request(请求) . response (响应). session (会话). application (应用程序). out . pageContex ...
- html面页与JAVA通过webSocket 通讯
(原) 往常前后端通讯基本都是以ajax请求或是表单做数据交互的,这是一种无状态的http协议,如果要做tcp协议的数据交互,能想到的技术也就socket了,可如果后端是JAVA,前端如何做socke ...
- 2.05-random-uesr-proxy
import urllib.request def proxy_user(): proxy_list = [ {"https":""}, # {"ht ...
- 关于HashMap的一些深入探索与理解
在java中.大家差点儿是离不开对集合的使用的,像Map系列,List系列.Set系列.可是非常多人没有了解过或者研究过这些集合类究竟能够用在什么地方,而且有什么注意的地方.因此本文分Map系列和Li ...
- redis加锁
1. redis加锁分类 redis能用的的加锁命令分表是INCR.SETNX.SET2. 第一种锁命令INCR 这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 ...
- TensorFlow 学习资料
感谢几位大神的笔记: https://blog.csdn.net/column/details/13300.html?&page=3 https://blog.csdn.net/u014595 ...
- WPF 格式化输出- IValueConverter接口的使用 datagrid列中的值转换显示
以前在用ASP.NET 做B/S系统时,可以方便地在GRIDVIEW DATAList等数据控件中,使用自定义的代码逻辑,比如 使用 <%# GetBalance(custID) %> 这 ...
- 爬取伯乐在线文章(二)通过xpath提取源文件中需要的内容
爬取说明 以单个页面为例,如:http://blog.jobbole.com/110287/ 我们可以提取标题.日期.多少个评论.正文内容等 Xpath介绍 1. xpath简介 (1) xpath使 ...
- ORA-08104
https://blog.csdn.net/daiqiulong2 create index idx_p_merchant_detail_id on D_ORDER_DETAIL (merchant_ ...