总结——数论:解高次同余方程 BSGS算法

解高次同余方程:已知 A B C(C为素数) , 求方程 Ax≡B(mod C) 的最小x值。

1. 解范围分析

  由欧拉函数的性质可知,对于素数C,φ(C)=C-1。

  又因 C 为素数,所以 (A,C)=1 , 由欧拉定理可知,Aφ(C)≡1(mod C).//或者直接由费马小定理可知,AC-1≡1(mod C)。

  又因已知 A0=1 , 即 A0≡1(mod C) , 所以对于方程的解 x , 0~φ(C) 为一个循环节(不一定是最小循环节)。

  最坏的情况下,A0 到 Aφ(C) 对C取模结果互不相同,所以方程的解的范围为 [0,φ(C)) 即 [0,C-1).

  如果C的范围很大,如: [2,231) , 暴搜O(C)肯定超时。

  一次同余方程 ax=b(mod m) , a≠0(mod m) 有解的充要条件是 (a,m)|b 。但是因为我是个蒟蒻,高次同余方程有解的条件找了半天也没看到证明,本文应该是用验证法验证模m的一个完全剩余系中所有数带入同余方程逐一验证所有数之后判断是否有解。

 

2. 优化暴搜——BSGS法(baby-step-giant-step)。

  令 x=i*m-j (注意:不要令 x=i*m+j , 否则需要求 a-m , 通过扩展欧几里得求 am 的逆元),其中 m=ceil(sqrt(C)) (ceil向上取整),所以x的范围小于C。

  则原式可以化为 Ai*m≡B*Aj(mod C)  式(1)。(此时若二层循环枚举i、j,则相当于枚举所有x)

  为什么叫大步小步法(当然如果你想叫拔山盖世法我也无发可说...),是因为对于j是走小步1,对于i每次走大步m。

  枚举 j(范围0-m),将 B*Aj mod C存入hash表。

  然后再枚举 i(范围1-m),从hash表中找出第一个满足式(1)的公式,此时 x=i*m-j 为所求。

  两次枚举的复杂度都是O(√C). (216=65536)。

  注意BSGS等式两边都计算了 B*Aj mod C , 存储的时候已经取模,所以不需要像普通同余方程利用扩展欧几里得求解,而是直接比较对幂取模的结果。

 

3. 复杂度分析和hash表

  其实我对于这个优化有点奇怪,看到很多人提到BSGS的优化是由于分块...其实也可以这么说,但是主要是因为hash这种结构。

  对于正常的数组而言,寻址容易O(1),查询元素困难O(n),为了简化查询元素的复杂度,我们将数据内容与存放地址(下标)建立映射,hash表就是通过这种映射建立的存储关系。如果忽略处理冲突,我们可以由查找的内容用O(1)的复杂度直接找到存储位置,而无需遍历数组。

  对于该同余方程,暴搜的复杂度显然不合适,我们对数据规模进行开方然后两层循环实际上复杂度不变,所以为了降低复杂度,我们采用时间换空间,将一部分数据进行存储到hash表中,所以外层循环开放的复杂度遍历,内层利用hashO(1)查询验证,这样复杂度就降下来了。

 

题目/模板:POJ 2417 Discrete Logging

 

posted @ 2018-03-24 13:18  Travelller  阅读(1252)  评论(0编辑  收藏  举报