先给出我所参考的两个链接:

http://hi.baidu.com/aekdycoin/item/236937318413c680c2cf29d4 (AC神,数论帝  扩展Baby Step Giant Step解决离散对数问题)

http://blog.csdn.net/a601025382s/article/details/11747747

Baby Step Giant Step算法:复杂度O( sqrt(C) )

我是综合上面两个博客,才差不多懂得了该算法。

先给出AC神的方法:

原创帖!转载请注明作者 AekdyCoin !

【普通Baby Step Giant Step】

【问题模型】
求解
A^x = B (mod C) 中 0 <= x < C 的解,C 为素数

【思路】
我们可以做一个等价
x = i * m + j ( 0 <= i < m, 0 <=j < m) m = Ceil ( sqrt( C) )
而这么分解的目的无非是为了转化为:
(A^i)^m * A^j = B ( mod C)

之后做少许暴力的工作就可以解决问题:
(1) for i = 0 -> m, 插入Hash (i, A^i mod C)
(2) 枚举 i ,对于每一个枚举到的i,令 AA = (A^m)^i mod C
我们有
AA * A^j = B (mod C)
显然AA,B,C均已知,而由于C为素数,那么(AA,C)无条件为1
于是对于这个模方程解的个数唯一(可以利用扩展欧几里得或 欧拉定理来求解)
那么对于得到的唯一解X,在Hash表中寻找,如果找到,则返回 i * m + j
注意:由于i从小到大的枚举,而Hash表中存在的j必然是对于某个剩余系内的元素X 是最小的(就是指标)
所以显然此时就可以得到最小解

如果需要得到 x > 0的解,那么只需要在上面的步骤中判断 当 i * m + j > 0 的时候才返回

到目前为止,以上的算法都不存在争议,大家实现的代码均相差不大。可见当C为素数的时候,此类离散对数的问题可以变得十分容易实现。

【扩展Baby Step Giant Step】

【问题模型】
求解
A^x = B (mod C) 中 0 <= x < C 的解,C 无限制(当然大小有限制……)

【写在前面】
这个问题比较麻烦,目前网络上流传许多版本的做法,不过大部分已近被证明是完全错误的!

这里就不再累述这些做法,下面是我的做法(有问题欢迎提出)

下面先给出算法框架,稍后给出详细证明:

() for i =  ->  if(A^i mod C == B) return i    O()
() d= D= mod C
while((tmp=gcd(A,C))!=)
{
if(B%tmp)return -; // 无解!
++d;
C/=tmp;
B/=tmp;
D=D*A/tmp%C; //不理解处1
}
() m = Ceil ( sqrt(C) ) //Ceil是必要的 O(1)
() for i = -> m 插入Hash表(i, A^i mod C) O( m)
() K=pow_mod(A,m,C)
for i = -> m
解 D * X = B (mod C) 的唯一解 (如果存在解,必然唯一!)
之后Hash表中查询,若查到(假设是 j),则 return i * m + j + d //不理解处2
否则
D=D*K%C,继续循环
() 无条件返回 - ;//无解!

上面有两处我一开始不理解的地方,稍后我会解释,先把AC神的方法给copy完

下面是证明:
推论1:
A^x = B(mod C)
等价为
A^a * A^b = B ( mod C) (a+b) == x a,b >= 0

证明:
A^x = K * C + B (模的定义)
A^a * A^b = K*C + B( a,b >=0, a + b == x)
所以有
A^a * A^b = B(mod C)
推论 2:
令 AA * A^b = B(mod C)
那么解存在的必要条件为: 可以得到至少一个可行解 A^b = X (mod C)
使上式成立
推论3
AA * A^b = B(mod C)
中解的个数为 (AA,C)
由推论3 不难想到对原始Baby Step Giant Step的改进
For I = 0 -> m
For any solution that AA * X = B (mod C)
If find X
Return I * m + j+d(这里原本少了d
而根据推论3,以上算法的复杂度实际在 (AA,C)很大的时候会退化到几乎O(C)
归结原因,是因为(AA,C)过大,而就是(A,C)过大
于是我们需要找到一中做法,可以将(A,C)减少,并不影响解

下面介绍一种“消因子”的做法

一开始D = 1 mod C
进行若干论的消因子,对于每次消因子
令 G = (A,C[i]) // C[i]表示经过i轮消因子以后的C的值
如果不存在 G | B[i] //B[i]表示经过i轮消因子以后的B的值
直接返回无解
否则
B[i+1] = B[i] / G
C[i+1] = C[i] / G
D = D * A / G

具体实现只需要用若干变量,细节参考代码

假设我们消了a'轮(假设最后得到的B,C分别为B',C')
那么有
D * A^b = B' (mod C')

于是可以得到算法

for i = 0 -> m
解 ( D* (A^m) ^i ) * X = B'(mod C')
由于 ( D* (A^m) ^i , C') = 1 (想想为什么?)
于是我们可以得到唯一解
之后的做法就是对于这个唯一解在Hash中查找

这样我们可以得到b的值,那么最小解就是a' + b !!

现在问题大约已近解决了,可是细心看来,其实还是有BUG的,那就是
对于
A^x = B(mod C)
如果x的最小解< a',那么会出错
而考虑到每次消因子最小消 2
故a'最大值为log(C)
于是我们可以暴力枚举0->log(C)的解,若得到了一个解,直接返回
否则必然有 解x > log(C)

PS.以上算法基于Hash 表,如果使用map等平衡树维护,那么复杂度会更大

接下来我将讲解下面两处我一开始不理解的地方:

() for i =  ->  if(A^i mod C == B) return i    O()
() d= D= mod C
while((tmp=gcd(A,C))!=)
{
if(B%tmp)return -; // 无解!
++d;
C/=tmp;
B/=tmp;
D=D*A/tmp%C; //不理解处1
}
() m = Ceil ( sqrt(C) ) //Ceil是必要的 O(1)
() for i = -> m 插入Hash表(i, A^i mod C) O( m)
() K=pow_mod(A,m,C)
for i = -> m
解 D * X = B (mod C) 的唯一解 (如果存在解,必然唯一!)
之后Hash表中查询,若查到(假设是 j),则 return i * m + j + d //不理解处2
否则
D=D*K%C,继续循环
() 无条件返回 - ;//无解!

后来看了上面给的第二个博客中的解释(被作者附在代码的后面),瞬间明白了啊!!!

下面给出那个博客里写的内容,做些适当改动,看着舒服点:

求解模方程A^x=B(mod C),C不为素数。拓展Baby Step Giant Step
(该作者的方法一开始没有暴力先枚举答案,即没有AC神的第(0)步,直接从第(1)步开始了。)
初始D=1,d=0,i=0;
1.令tmp=gcd(A,C),若tmp==1则执行下一步。否则由于A^x=k*C+B;(k为某一整数),则(A/tmp)*A^(x-1)=k*(C/tmp)+B/tmp,(B/tmp为整除,若不成立则无解)
令C=C/tmp,D=D*A/tmp,B=B/tmp,d++。则D*A^(x-d)=B(mod C),接着重复1步骤。
看到没,while循环的原因瞬间明白了不?d的含义瞬间懂了有木有!!!也就是后面求DX=B(mod C)的解其实是x-d!!!所以最后才要i*m+j+d,加上个d!!!
2.通过1步骤后,保证了A和D都与C互质,方程转换为D*A^(x-d)=B(mod C)。由于A和C互质,所以由欧拉定理A^phi(C)==1(mod C),(A,C互质)
可知,phi(C)<=C,A^0==1(mod C),所以构成循环,且循环节不大于C。从而推出如果存在解,则必定1<=x<C。(在此基础上我们就可以用
Baby Step Giant Step方法了)
3.令m=ceil(sqrt(C)),则m*m>=C。用哈希表存储A^0,A^1,..,A^(m-1),接着判断1~m*m-1中是否存在解。
4.为了减少时间,所以用哈希表缩减复杂度。分成m次遍历,每次遍历A^m长度。由于A和D都与C互质,所以gcd(D,C)=1。
接下来实际求DX=B(mod C)的解。用拓展的欧几里德定理求得DX=1(mod C),即D*x+C*y=gcd(D,C)=1的一组整数解(x,y)。
则D*x+C*y=1-->D*x%C=(D*x+C*y)%C=1-->D*(x*B)%C=((D*x)%C*B%C)%C=B。
所以最终DX=B(mod C)的解X=x*B。若X在哈希表中存在,值为j,则D*A^j=B(mod C),最后我们要求解的答案就是ans=i*m+j+d。
如果不存在,则令D=D*A^m%C,i++后遍历下一个A^m,直到遍历(A^m)^0到(A^m)^(m-1)还未找到,则说明不解并退出。

本人注明:该博客中i循环是0~m-1,在AC神的方法中是0~m,本人觉得应该前者比较好。因为解x是1<=x<C的,而m=ceil(sqrt(C)),如果i=m的话,那m*m>C,显然不正确。

下面是AC神的两道题的代码:

HDU 2815

#include<iostream>
#include<map>
#include<cmath>
using namespace std;
typedef long long LL;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int ext_gcd(int a,int b,int& x,int& y){
int t,ret;
if (!b){x=,y=;return a;}
ret=ext_gcd(b,a%b,x,y);
t=x,x=y,y=t-a/b*y;
return ret;
}
int Inval(int a,int b,int n){
int x,y,e;
ext_gcd(a,n,x,y);
e=(LL)x*b%n;
return e<?e+n:e;
}
int pow_mod(LL a,int b,int c){LL ret=%c;a%=c;while(b){if(b&)ret=ret*a%c;a=a*a%c;b>>=;}return ret;}
int BabyStep(int A,int B,int C){
map<int,int> Hash;
LL buf=%C,D=buf,K;
int i,d=,tmp;
for(i=;i<=;buf=buf*A%C,++i)if(buf==B)return i;
while((tmp=gcd(A,C))!=)
{
if(B%tmp)return -;
++d;
C/=tmp;
B/=tmp;
D=D*A/tmp%C;
}
Hash.clear();
int M=(int)ceil(sqrt((double)C));
for(buf=%C,i=;i<=M;buf=buf*A%C,++i)if(Hash.find((int)buf)==Hash.end())Hash[(int)buf]=i;
for(i=,K=pow_mod((LL)A,M,C);i<=M;D=D*K%C,++i)
{
tmp=Inval((int)D,B,C);
if(tmp>=&&Hash.find(tmp)!=Hash.end())return i*M+Hash[tmp]+d;
}
return -;
}
int main()
{
int A,B,C;
while(scanf("%d%d%d",&A,&C,&B)!=EOF)
{
if(B>=C){puts("Orz,I can’t find D!");continue;}
int tmp=BabyStep(A,B,C);
if(tmp<)puts("Orz,I can’t find D!");else printf("%d\n",tmp);
}
return ;
}

POJ 3243 Clever Y

#include<iostream>
#include<map>
#include<cmath>
using namespace std;
typedef long long LL;
const int maxn = ;
struct hash{
int a,b,next;
}Hash[maxn << ];
int flg[maxn + ];
int top,idx;
void ins(int a,int b){
int k = b & maxn;
if(flg[k] != idx){
flg[k] = idx;
Hash[k].next = -;
Hash[k].a = a;
Hash[k].b = b;
return ;
}
while(Hash[k].next != -){
if(Hash[k].b == b) return ;
k = Hash[k].next;
}
Hash[k].next = ++ top;
Hash[top].next = -;
Hash[top].a = a;
Hash[top].b = b;
}
int find(int b){
int k = b & maxn;
if(flg[k] != idx) return -;
while(k != -){
if(Hash[k].b == b) return Hash[k].a;
k = Hash[k].next;
}
return -;
}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int ext_gcd(int a,int b,int& x,int& y){
int t,ret;
if (!b){x=,y=;return a;}
ret=ext_gcd(b,a%b,x,y);
t=x,x=y,y=t-a/b*y;
return ret;
}
int Inval(int a,int b,int n){
int x,y,e;
ext_gcd(a,n,x,y);
e=(LL)x*b%n;
return e<?e+n:e;
}
int pow_mod(LL a,int b,int c){LL ret=%c;a%=c;while(b){if(b&)ret=ret*a%c;a=a*a%c;b>>=;}return ret;}
int BabyStep(int A,int B,int C){
top = maxn; ++ idx;
LL buf=%C,D=buf,K;
int i,d=,tmp;
for(i=;i<=;buf=buf*A%C,++i)if(buf==B)return i;
while((tmp=gcd(A,C))!=){
if(B%tmp)return -;
++d;
C/=tmp;
B/=tmp;
D=D*A/tmp%C;
}
int M=(int)ceil(sqrt((double)C));
for(buf=%C,i=;i<=M;buf=buf*A%C,++i)ins(i,buf);
for(i=,K=pow_mod((LL)A,M,C);i<=M;D=D*K%C,++i){
tmp=Inval((int)D,B,C);int w ;
if(tmp>=&&(w = find(tmp)) != -)return i*M+w+d;
}
return -;
}
int main(){
int A,B,C;
while(scanf("%d%d%d",&A,&C,&B)!=EOF,A || B || C){
B %= C;
int tmp=BabyStep(A,B,C);
if(tmp<)puts("No Solution");else printf("%d\n",tmp);
}
return ;
}

解高次同余方程 (A^x=B(mod C),0<=x<C)Baby Step Giant Step算法的更多相关文章

  1. 【解高次同余方程】51nod1038 X^A Mod P

    1038 X^A Mod P 基准时间限制:1 秒 空间限制:131072 KB 分值: 320 X^A mod P = B,其中P为质数.给出P和A B,求< P的所有X. 例如:P = 11 ...

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

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

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

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

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

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

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

  6. 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 Subm ...

  7. 高次同余方程模板BabyStep-GiantStep

    /************************************* ---高次同余方程模板BabyStep-GiantStep--- 输入:对于方程A^x=B(mod C),调用BabySt ...

  8. BZOJ 2480 && 3239 && 2995 高次不定方程(高次同余方程)

    链接 BZOJ 2480 虽然是个三倍经验题(2333),但是只有上面这道(BZOJ2480)有 p = 1 的加强数据,推荐大家做这道. 题解 这是一道BSGS(Baby Step Giant St ...

  9. BSGS算法_Baby steps giant steps算法(无扩展)详解

    Baby Steps-Varsity Giant Step-Astronauts(May'n・椎名慶治) 阅读时可以听听这两首歌,加深对这个算法的理解.(Baby steps少女时代翻唱过,这个原唱反 ...

随机推荐

  1. ED/EP系列1《简介》

    电子存折(ED:ElectronicDeposit)一种为持卡人进行消费.取现等交易而设计的支持个人识别码(PIN)保护的金融IC卡应用.它支持圈存.圈提.消费和取现等交易. 电子钱包(EP:Elec ...

  2. ES6 入门系列 - let 和 const 命令

    let命令 基本用法 ES6新增了let命令,用来声明变量.它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效. { let a = ; ; } a // ReferenceEr ...

  3. ES5 vs ES6

    ES5中 var React = require('react-native'); ES6中 import React from 'react-native'; .babelrc文件中添加一下内容 { ...

  4. Objective-C-实例变量与属性的关系

    当在一个类创建一个属性,Xcode编译器就会自动产生一个带下划线的同名实例变量: 一般来说,如果getter这个属性采用下划线的方式获取效率更高,而setter采用self.属性名更加合理. 读取实例 ...

  5. kafka中server.properties配置文件参数说明

    转自:http://blog.csdn.net/lizhitao/article/details/25667831 参数 说明(解释) broker.id =0 每一个broker在集群中的唯一表示, ...

  6. [原创]基于html5新标签canvas写的一个小画板

    最近刚学了canvas,写个小应用练习下 源代码 <!DOCTYPE> <html> <head> <meta http-equiv="Conten ...

  7. 5.防止FPGA设计中综合后的信号被优化

    随着FPGA设计复杂程度越来越高,芯片内部逻辑分析功能显得越来越重要.硬件层次上的逻辑分析仪价格十分昂贵,而且操作比较复杂.目前,FPGA芯片的两大供应商都为自己的FPGA芯片提供了软件层面上的逻辑分 ...

  8. c++11并发程序设计(1)

    第一章:你好 c++的并发世界 1.何为并发 最简单和最基本的并发,是指两个或两个以上的独立活动同时进行. 对于单个处理单元或者核心,这种机器只能在某一时刻执行一个任务,不够它可以每秒进行多次的任务切 ...

  9. 几种常见的排序方法(C语言实现)

    #include <stdio.h> #include <stdlib.h> #include <Windows.h> //直接插入排序 void InsertSo ...

  10. C++中常见的几种异常类型

    1.C++具有完善的异常捕获机制,采用try{}  catch(){}机制,这是C语言无法比拟的 2.常见的几种异常: bad_alloc:       请求分配内存失败, operator new ...