离散对数和BSGS算法

  设$x$是最小的非负整数使得$a^{x}\equiv b\ \ \ \pmod{m}$,则$x$是$b$以$a$为底的离散对数,记为$x = ind_{a}b$。

  假如给定$a, b, m$,考虑如何求$x$,或者输出无解,先考虑$(a, m) = 1$的情况。

定理1(欧拉定理) 若$(a, m) = 1$,则$a^{\varphi(m)}\equiv 1 \pmod{m}$。

  证明这里就不给出,因为在百度上随便搜一搜就能找到。

  不过,这个定理告诉我们,在$(a, m) = 1$的情况下,若存在答案,则答案不会超过$\varphi(m) - 1$。

  考虑$a^{x} \equiv b \pmod{m}$,通过一些操作可以得到:

$a^{x - k} \equiv a^{-k}b \pmod{m}$

  因此可以选取正整数$c$,将$x$表示为$ic + j$的形式,然后有:

$a^{ic} \equiv a^{-j}b \pmod{m}$

  考虑预处理$a^{-j}b$,以它的值为键,最小的$j$为值存入Hash表或者Map中。

  这样有什么用呢?你可以快速枚举$a^{ic}$,然后你将这个值在Hash表中查一查对应的最小的$j$,如果查到就可以得到答案了。

Code

 /**
* poj
* Problem#2417
* Accepted
* Time: 16ms
* Memory: 1372l
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef bool boolean; int p, x, a; typedef class HashMap {
private:
static const int M = ;
public:
int ce;
int h[M], key[M], val[M], next[M]; HashMap():ce(-) { } void insert(int k, int v) {
int ha = k % M;
for (int i = h[ha]; ~i; i = next[i])
if (key[i] == k) {
val[i] = v;
return;
}
++ce, key[ce] = k, val[ce] = v, next[ce] = h[ha];
h[ha] = ce;
} int operator [] (int k) {
int ha = k % M;
for (int i = h[ha]; ~i; i = next[i])
if (key[i] == k)
return val[i];
return -;
} void clear() {
ce = -;
memset(h, -, sizeof(h));
}
}HashMap; int qpow(int a, int pos) {
int pa = a, rt = ;
for (; pos; pos >>= , pa = pa * 1ll * pa % p)
if (pos & )
rt = rt * 1ll * pa % p;
return rt;
} void exgcd(int a, int b, int& d, int &x, int &y) {
if (!b)
d = a, x = , y = ;
else {
exgcd(b, a % b, d, y, x);
y -= (a / b) * x;
}
} int inv(int a, int n) {
int d, x, y;
exgcd(a, n, d, x, y);
return (x < ) ? (x + n) : (x);
} inline boolean init() {
return ~scanf("%d%d%d", &p, &x, &a);
} int cs;
HashMap mp;
inline int ind() {
mp.clear();
cs = sqrt(p - + 0.5);
if (cs == ) cs++;
int ainv = inv(x, p), iap = a * 1ll * qpow(ainv, cs - ) % p;
for (int i = cs - ; ~i; i--, iap = iap * 1ll * x % p)
mp.insert(iap, i);
int cp = qpow(x, cs), pw = ;
for (int i = ; i < p; i += cs, pw = pw * 1ll * cp % p)
if (~mp[pw])
return mp[pw] + i;
return -;
} inline void solve() {
int res = ind();
if (res == -)
puts("no solution");
else
printf("%d\n", res);
} int main() {
while (init())
solve();
return ;
}

BSGS

扩展BSGS算法

  使刚刚的问题更一般,去掉$(a, m) = 1$的条件。

  此时逆元不一定存在,所以不能用上述的BSGS算法来做。

  考虑去掉它们的公约数$d$。

  得到

$a^{x - 1}\cdot\frac{a}{d} \equiv \frac{b}{d} \pmod{\frac{m}{d}}$

  在这步中,如果$b \nmid d$,那么显然无解。

  否则我可以令$x' = x - 1,k' = \frac{a}{d}, b'=\frac{b}{d}, m' = \frac{m}{d}$进行换元得到:

$k'a^{x'} \equiv b' \pmod{m'}$

  如果$(a, m') = 1$,那么直接BSGS解这个方程,然后带回去算原先的$x$,否则可以继续计算$a$和$m'$的最大公约数,继续除掉它,直到$(a, m) = 1$,然后BSGS解方程。

  因为最大公约数不为1,每次至少除以2,1和任何数互质,因此总共除的次数不会超过$\log_{2}m$。

  但是这么做存在一个问题,假如除的次数为$k$,那么它会忽略大于等于0小于$k$的解,因此,除的时候判一下即可。

Code

 /**
* poj
* Problem#3243
* Accepted
* Time: 47ms
* Memory: 1248k
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#ifndef WIN32
#define Auto "%lld"
#else
#define Auto "%I64d"
#endif
using namespace std;
typedef bool boolean; int x, a, m; typedef class HashMap {
private:
static const int M = ;
public:
int ce;
int h[M], key[M], val[M], next[M]; HashMap():ce(-) { } void insert(int k, int v) {
int ha = k % M;
for (int i = h[ha]; ~i; i = next[i])
if (key[i] == k) {
val[i] = v;
return;
}
++ce, key[ce] = k, val[ce] = v, next[ce] = h[ha];
h[ha] = ce;
} int operator [] (int k) {
int ha = k % M;
for (int i = h[ha]; ~i; i = next[i])
if (key[i] == k)
return val[i];
return -;
} void clear() {
ce = -;
memset(h, -, sizeof(h));
}
}HashMap; int qpow(int a, int pos, int m) {
int pa = a, rt = ;
for (; pos; pos >>= , pa = pa * 1ll * pa % m)
if (pos & )
rt = rt * 1ll * pa % m;
return rt;
} int gcd (int a, int b) {
return (b) ? (gcd(b, a % b)) : (a);
} void exgcd(int a, int b, int& d, int &x, int &y) {
if (!b)
d = a, x = , y = ;
else {
exgcd(b, a % b, d, y, x);
y -= (a / b) * x;
}
} int inv(int a, int n) {
int d, x, y;
exgcd(a, n, d, x, y);
return (x < ) ? (x + n) : (x);
} inline boolean init() {
return ~scanf("%d%d%d", &x, &m, &a) && (x || m || a);
} int cs;
HashMap mp;
inline int ind(int pro, int x, int a, int p) {
mp.clear();
cs = sqrt(p - + 0.5);
int ainv = inv(x, p), iap = a * 1ll * qpow(ainv, cs - , p) % p;
for (int i = cs - ; ~i; i--, iap = iap * 1ll * x % p)
mp.insert(iap, i);
int cp = qpow(x, cs, p), pw = pro;
for (int i = ; i < p; i += cs, pw = pw * 1ll * cp % p)
if (~mp[pw])
return mp[pw] + i;
return -;
} int exind(int x, int a, int m) {
if (a == ) return ;
int d, k = , pro = ;
while ((d = gcd(x, m)) != ) {
if (a % d) return -;
if (pro == a) return k;
a /= d, m /= d, pro = (pro * 1ll * (x / d)) % m, k++;
}
int rt = ind(pro, x % m, a, m);
return (~rt) ? (rt + k) : (-);
} inline void solve() {
int res = exind(x % m, a % m, m);
if (res == -)
puts("No Solution");
else
printf("%d\n", res);
} int main() {
while (init())
solve();
return ;
}

ex-BSGS

BSGS算法学习笔记的更多相关文章

  1. C / C++算法学习笔记(8)-SHELL排序

    原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...

  2. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  3. Johnson算法学习笔记

    \(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...

  4. 某科学的PID算法学习笔记

    最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...

  5. Johnson 全源最短路径算法学习笔记

    Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...

  6. 算法学习笔记——sort 和 qsort 提供的快速排序

    这里存放的是笔者在学习算法和数据结构时相关的学习笔记,记录了笔者通过网络和书籍资料中学习到的知识点和技巧,在供自己学习和反思的同时为有需要的人提供一定的思路和帮助. 从排序开始 基本的排序算法包括冒泡 ...

  7. R语言实现关联规则与推荐算法(学习笔记)

    R语言实现关联规则 笔者前言:以前在网上遇到很多很好的关联规则的案例,最近看到一个更好的,于是便学习一下,写个学习笔记. 1 1 0 0 2 1 1 0 0 3 1 1 0 1 4 0 0 0 0 5 ...

  8. 二次剩余Cipolla算法学习笔记

    对于同余式 \[x^2 \equiv n \pmod p\] 若对于给定的\(n, P\),存在\(x\)满足上面的式子,则乘\(n\)在模\(p\)意义下是二次剩余,否则为非二次剩余 我们需要计算的 ...

  9. SPFA算法学习笔记

    一.理论准备 为了学习网络流,先水一道spfa. SPFA算法是1994年西南交通大学段凡丁提出,只要最短路径存在,SPFA算法必定能求出最小值,SPFA对Bellman-Ford算法优化的关键之处在 ...

随机推荐

  1. Request.UrlReferrer注意点

    定义: public sealed class HttpRequest { // // 摘要: // 获取有关客户端上次请求的 URL 的信息,该请求链接到当前的 URL. // // 返回结果: / ...

  2. Anaconda 使用指南

    Anaconda 使用指南 参考文章: 致Python初学者:Anaconda入门使用指南 Anaconda使用总结 概述 很多学习python的初学者甚至学了有一段时间的人接触到anaconda或者 ...

  3. matlab 字符串处理函数

    https://www.cnblogs.com/emanlee/archive/2012/09/13/2683912.html % 字符串处理 a='  a';b='b  b';c='cccc';m= ...

  4. MyBatis基础入门《十二》删除数据 - @Param参数

    MyBatis基础入门<十二>删除数据 - @Param参数 描述: 删除数据,这里使用了@Param这个注解,其实在代码中,不使用这个注解也可以的.只是为了学习这个@Param注解,为此 ...

  5. .NET 黑魔法 - 自定义日志扩展

    我们开发程序时避免不了要有日志系统,我们希望有一个通用的.不夹杂任何方言的调用方式,简单地说就是保留微软日志框架的注入方式和使用方式. 比如我们希望这样调用: 我们不希望有个 IAbcLogger, ...

  6. install scala & spark env

    安装Scala 1,到http://www.scala-lang.org/download/ 下载与Spark版本对应的Scala.Spark1.2对应于Scala2.10的版本.这里下载scala- ...

  7. <3>Cocos Creator编辑器基础

    Cocos Creator编辑器界面主要窗口包含如下: * 资源管理器窗口 * 场景编辑器窗口 * 层级管理器窗口 * 属性检查器窗口 * 上方功能按钮 * 偏好设置 * 串口输出 * 预览和构建 1 ...

  8. JDBC操作数据库步骤

    2018-11-04  20:23:24开始写 1.加载驱动程序(Class.forName) 2.建立连接获取数据库连接对象(DriverManager.getConnection) 3.向数据库发 ...

  9. [openjudge-动态规划]怪盗基德的滑翔翼

    题目描述 描述 怪盗基德是一个充满传奇色彩的怪盗,专门以珠宝为目标的超级盗窃犯.而他最为突出的地方,就是他每次都能逃脱中村警部的重重围堵,而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼. 有一天 ...

  10. 转:【专题七】UDP编程补充——UDP广播程序的实现

    上次因为时间的关系,所以把上一个专题遗留下的一个问题在本专题中和大家分享下,本专题主要介绍下如何实现UDP广播的程序,下面就直接介绍实现过程和代码以及运行的结果. 一.程序实现 UDP广播程序的实现代 ...