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 很酷的算法的更多相关文章

  1. 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 ...

  2. 解高次同余方程 (A^x=B(mod C),0<=x<C)Baby Step Giant Step算法

    先给出我所参考的两个链接: http://hi.baidu.com/aekdycoin/item/236937318413c680c2cf29d4 (AC神,数论帝  扩展Baby Step Gian ...

  3. HDU 2815 Mod Tree 离散对数 扩张Baby Step Giant Step算法

    联系:http://acm.hdu.edu.cn/showproblem.php?pid=2815 意甲冠军: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQ ...

  4. 『高次同余方程 Baby Step Giant Step算法』

    高次同余方程 一般来说,高次同余方程分\(a^x \equiv b(mod\ p)\)和\(x^a \equiv b(mod\ p)\)两种,其中后者的难度较大,本片博客仅将介绍第一类方程的解决方法. ...

  5. HDU 2815 扩展baby step giant step 算法

    题目大意就是求 a^x = b(mod c) 中的x 用一般的baby step giant step 算法会超时 这里参考的是http://hi.baidu.com/aekdycoin/item/2 ...

  6. 【学习笔记】Baby Step Giant Step算法及其扩展

    1. 引入 Baby Step Giant Step算法(简称BSGS),用于求解形如\(a^x\equiv b\pmod p\)(\(a,b,p\in \mathbb{N}\))的同余方程,即著名的 ...

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

    什么叫高次同余方程?说白了就是解决这样一个问题: A^x=B(mod C),求最小的x值. baby step giant step算法 题目条件:C是素数(事实上,A与C互质就可以.为什么?在BSG ...

  8. [置顶] hdu2815 扩展Baby step,Giant step入门

    题意:求满足a^x=b(mod n)的最小的整数x. 分析:很多地方写到n是素数的时候可以用Baby step,Giant step, 其实研究过Baby step,Giant step算法以后,你会 ...

  9. POJ 2417 Discrete Logging ( Baby step giant step )

    Discrete Logging Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 3696   Accepted: 1727 ...

  10. 【POJ2417】baby step giant step

    最近在学习数论,然而发现之前学的baby step giant step又忘了,于是去翻了翻以前的代码,又复习了一下. 觉得总是忘记是因为没有彻底理解啊. 注意baby step giant step ...

随机推荐

  1. 下载时出现using cached如何解决

    pip3 --no-cache-dir install xlsxwriter 这样就可以了

  2. (1)Python基础

    几种常用类型 int float str bool 基本数值操作 绝对值 abs 四舍五入 round 最大值&最小值

  3. UVA12265-Selling Land(细节处理)

    Problem UVA12265-Selling Land Accept: 309  Submit: 3231Time Limit: 3000 mSec Problem Description Inp ...

  4. WPF之DataGrid应用 翻页

    前几天打算尝试下DataGrid的用法,起初以为应该很简单,可后来被各种使用方法和功能实现所折磨.网络上的解决方法太多,但也太杂.没法子,我只好硬着头皮阅览各种文献资料,然后不断的去尝试,总算小有成果 ...

  5. Oracle使用数据泵 (expdp/impdp)实施迁移

    实验环境: 1.导出环境:RedHat6.4+Oracle 11.2.0.4.0,利用数据库自带的scott示例用户进行试验测试. Directory:wjq à /tmp/seiang_wjq 2. ...

  6. 【转】Android hdpi ldpi mdpi xhdpi xxhdpi适配详解

    1.了解几个概念(1)分辨率.分辨率就是手机屏幕的像素点数,一般描述成屏幕的“宽×高”,安卓手机屏幕常见的分辨率有480×800.720×1280.1080×1920等.720×1280表示此屏幕在宽 ...

  7. Topshelf:一款非常好用的 Windows 服务开发框架 转发https://www.cnblogs.com/happyframework/p/3601995.html

    背景 多数系统都会涉及到“后台服务”的开发,一般是为了调度一些自动执行的任务或从队列中消费一些消息,开发 windows service 有一点不爽的是:调试麻烦,当然你还需要知道 windows s ...

  8. Linux内存管理之mmap详解

    转发之:http://blog.chinaunix.net/uid-26669729-id-3077015.html Linux内存管理之mmap详解 一. mmap系统调用 1. mmap系统调用  ...

  9. Luogu P3321 [SDOI2015]序列统计

    一道不错的多项式好题.还涉及了一些数论内容. 首先我们看到题目是求乘积模\(m\)的方案数,考虑到这种方案数我们一般都可以用生成函数来做. 但显然卷积的下标有加(FFT,NTT等)有位运算(FWT)但 ...

  10. 【重磅】Spring Boot 2.1.0 权威发布

    如果这两天登录 https://start.spring.io/ 就会发现,Spring Boot 默认版本已经升到了 2.1.0.这是因为 Spring Boot 刚刚发布了 2.1.0 版本,我们 ...