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. 【Linux基础】tr命令替换和删除字符

    1.tr命令 tr可以对来自标准输入的字符进行替换.压缩和删除,可以将一组字符变成另外一组字符.通过使用 tr,您可以非常容易地实现 sed 的许多最基本功能.您可以将 tr 看作为 sed 的(极其 ...

  2. [Java] SpringMVC工作原理之三:ViewResolver

    一.ViewResolver 根据视图的名称将其解析为 View 类型的视图,如通过 ModelAndView 中的视图名称将其解析成 View,View 是用来渲染页面的,也就是将 Model 填入 ...

  3. ES5-ES6-ES7_Promise对象详解

    Promise对象概述(什么是Promise) Promise 是异步编程的一种解决方案,比传统的异步解决方案——回调函数和事件——更合理和更强大 所谓Promise,简单说就是一个容器,里面保存着某 ...

  4. 寒假训练——搜索——C - Robot

    The Robot Moving Institute is using a robot in their local store to transport different items. Of co ...

  5. 安装Anaconda 之后使用ubuntu自带python

    我们知道,Ubantu系统会自带python,当你在terminal窗口中输入python,就会显示默认安装的python的信息.比如我的16.04就自带了python2.7和3.5,但是安装了Ana ...

  6. TensorFlow——循环神经网络基本结构

    1.导入依赖包,初始化一些常量 import collections import numpy as np import tensorflow as tf TRAIN_DATA = "./d ...

  7. iptables 从一台机到另一台机端口转发

    启用网卡转发功能#echo 1 > /proc/sys/net/ipv4/ip_forward 举例:从192.168.0.132:21521(新端口)访问192.168.0.211:1521端 ...

  8. Linux shell判断文件和文件夹是否存在(转发)

    #!/bin/sh myPath="/var/log/httpd/" myFile="/var /log/httpd/access.log" #这里的-x 参数 ...

  9. 配置DispatcherServlet应该写/还是/*

    相亲怎么做 web应用需要放在Tomcat容器中才能启动,Tomcat容器内有一个默认的web.xml文件,在自己项目中配置的XML文件都是继承自Tomcat中的全局XML文件并重写其中相应配置,这种 ...

  10. docker 4 docker的三要素

    docker三要素 镜像,容器,仓库 镜像 docker镜像(image)就是一个只读的模板,镜像可以用来创建docker容器,一个镜像可以创建很多个容器 容器 docker利用容器(containe ...