OSDN Git Service

style: format markdown files with remark-lint
author24OI-bot <15963390+24OI-bot@users.noreply.github.com>
Fri, 19 Jul 2019 14:04:30 +0000 (10:04 -0400)
committer24OI-bot <15963390+24OI-bot@users.noreply.github.com>
Fri, 19 Jul 2019 14:04:30 +0000 (10:04 -0400)
docs/math/bsgs.md

index e73c6e3..85e90ff 100644 (file)
@@ -6,7 +6,7 @@ $$
 a^x \equiv b \bmod p
 $$
 
-其中 $a\perp p$。方程的解 $x$ 满足 $0 \le x < p$ 。(在这里一定要强调,只要 $a\perp p$ 就行了,不要求 $p$ 是素数!)
+其中 $a\perp p$ 。方程的解 $x$ 满足 $0 \le x < p$ 。(在这里一定要强调,只要 $a\perp p$ 就行了,不要求 $p$ 是素数!)
 
 ### 算法描述
 
@@ -26,23 +26,23 @@ $$
 
 其中 $p$ 是个质数。
 
-该模型可以通过一系列的转化为成 **基础篇** 中的模型,你可能需要了解关于[阶与原根](/math/primitive-root/) 的知识。
+该模型可以通过一系列的转化为成 **基础篇** 中的模型,你可能需要了解关于[阶与原根](/math/primitive-root/)的知识。
 
-由于式子中的模数 $p$ 是一个质数,那么 $p$ 一定存在一个原根 $g$。因此对于模 $p$ 意义下的任意的数 $x\ (0\le x<p)$ 有且仅有一个数 $i\ (0\le i<p-1)$ 满足 $x = g^i$
+由于式子中的模数 $p$ 是一个质数,那么 $p$ 一定存在一个原根 $g$ 。因此对于模 $p$ 意义下的任意的数 $x\ (0\le x<p)$ 有且仅有一个数 $i\ (0\le i<p-1)$ 满足 $x = g^i$ 
 
 ### 方法一
 
-我们令 $x=g^c$ , $g$ 是 $p$ 的原根(我们一定可以找到这个 $g$ 和 $c$ ),问题转化为求解 $(g^c)^a \equiv b \bmod p$。稍加变换,得到
+我们令 $x=g^c$ , $g$ 是 $p$ 的原根(我们一定可以找到这个 $g$ 和 $c$ ),问题转化为求解 $(g^c)^a \equiv b \bmod p$ 。稍加变换,得到
 
 $$
 (g^a)^c \equiv b \mod p
 $$
 
-于是就转换成了我们熟知的 **BSGS** 的基本模型了,可以在 $O(\sqrt p)$ 解出 $c$,这样可以得到原方程的一个特解 $x_0\equiv g^c\bmod p$
+于是就转换成了我们熟知的 **BSGS** 的基本模型了,可以在 $O(\sqrt p)$ 解出 $c$ ,这样可以得到原方程的一个特解 $x_0\equiv g^c\bmod p$ 
 
 ### 方法二
 
-我们仍令 $x=g^c$,并且设 $b=g^t$,于是我们得到
+我们仍令 $x=g^c$ ,并且设 $b=g^t$ ,于是我们得到
 
 $$
 g^{ac}\equiv g^t\mod p
@@ -54,11 +54,11 @@ $$
 ac\equiv t\mod \varphi(p)
 $$
 
-我们可以通过 BSGS 求解 $g^t\equiv b\bmod p$ 得到 $t$,于是这就转化成了一个线性同余方程的问题。这样也可以解出 $c$,求出 $x$ 的一个特解 $x_0\equiv g^c\bmod p$
+我们可以通过 BSGS 求解 $g^t\equiv b\bmod p$ 得到 $t$ ,于是这就转化成了一个线性同余方程的问题。这样也可以解出 $c$ ,求出 $x$ 的一个特解 $x_0\equiv g^c\bmod p$ 
 
 ### 找到所有解
 
-在知道 $x_0\equiv g^{c}\pmod n$ 的情况下,我们想得到原问题的所有解。首先我们知道 $g^{\varphi(n)}\equiv 1\pmod n$,于是可以得到
+在知道 $x_0\equiv g^{c}\pmod n$ 的情况下,我们想得到原问题的所有解。首先我们知道 $g^{\varphi(n)}\equiv 1\pmod n$ ,于是可以得到
 
 $$
 \forall\ t \in \mathbb{Z},\ x^k \equiv g^{ c \cdot k + t\cdot\varphi(n)}\equiv a \mod p
@@ -70,7 +70,7 @@ $$
 \forall\ t\in \mathbb{Z},k\mid t\cdot\varphi(n),\ x\equiv g^{c+\frac{t\cdot\varphi(n)}{k}}\mod p
 $$
 
-对于上面这个式子,显然有 $\frac{k}{\gcd(k,\varphi(n))}\mid t$。因此我们设 $t=\frac{k}{\gcd(k,\varphi(n))}\cdot i$,得到
+对于上面这个式子,显然有 $\frac{k}{\gcd(k,\varphi(n))}\mid t$ 。因此我们设 $t=\frac{k}{\gcd(k,\varphi(n))}\cdot i$ ,得到
 
 $$
 \forall \ i\in \mathbb{Z},x\equiv g^{c+\frac{\varphi(n)}{\gcd(k,\varphi(n))}\cdot i}\mod p
@@ -83,67 +83,68 @@ $$
 下面的代码实现的找原根、离散对数解和原问题所有解的过程。
 
 ```cpp
-int gcd(int a,int b){ return a?gcd(b%a,a):b; }
+int gcd(int a, int b) { return a ? gcd(b % a, a) : b; }
 int powmod(int a, int b, int p) {
-    int res=1;
-    while(b>0) {
-        if(b&1)res=res*a%p;
-        a=a*a%p, b>>=1;
-    }
-    return res;
+  int res = 1;
+  while (b > 0) {
+    if (b & 1) res = res * a % p;
+    a = a * a % p, b >>= 1;
+  }
+  return res;
 }
 // Finds the primitive root modulo p
 int generator(int p) {
-    vector<int> fact;
-    int phi=p-1,n=phi;
-    for(int i=2; i*i<=n; ++i) {
-        if(n%i==0) {
-            fact.push_back(i);
-            while(n%i==0) n/=i;
-        }
+  vector<int> fact;
+  int phi = p - 1, n = phi;
+  for (int i = 2; i * i <= n; ++i) {
+    if (n % i == 0) {
+      fact.push_back(i);
+      while (n % i == 0) n /= i;
     }
-    if(n>1) fact.push_back(n);
-    for(int res=2; res<=p; ++res) {
-        bool ok=true;
-        for(int factor : fact) {
-            if(powmod(res, phi/factor, p)==1) {
-                ok=false;
-                break;
-            }
-        }
-        if(ok) return res;
+  }
+  if (n > 1) fact.push_back(n);
+  for (int res = 2; res <= p; ++res) {
+    bool ok = true;
+    for (int factor : fact) {
+      if (powmod(res, phi / factor, p) == 1) {
+        ok = false;
+        break;
+      }
     }
-    return -1;
+    if (ok) return res;
+  }
+  return -1;
 }
 // This program finds all numbers x such that x^k=a (mod n)
 int main() {
-    int n, k, a;
-    scanf("%d %d %d", &n, &k, &a);
-    if(a==0) return puts("1\n0"),0;
-    int g=generator(n);
-    // Baby-step giant-step discrete logarithm algorithm
-    int sq=(int) sqrt (n+.0)+1;
-    vector<pair<int, int>> dec(sq);
-    for(int i=1; i<=sq; ++i) dec[i-1]={powmod(g, i*sq*k%(n-1), n), i};
-    sort(dec.begin(), dec.end());
-    int any_ans=-1;
-    for(int i=0; i<sq; ++i) {
-        int my=powmod(g, i*k%(n-1), n)*a%n;
-        auto it=lower_bound(dec.begin(), dec.end(), make_pair(my, 0));
-        if(it != dec.end() && it->first==my) {
-            any_ans=it->second*sq-i;
-            break;
-        }
+  int n, k, a;
+  scanf("%d %d %d", &n, &k, &a);
+  if (a == 0) return puts("1\n0"), 0;
+  int g = generator(n);
+  // Baby-step giant-step discrete logarithm algorithm
+  int sq = (int)sqrt(n + .0) + 1;
+  vector<pair<int, int>> dec(sq);
+  for (int i = 1; i <= sq; ++i)
+    dec[i - 1] = {powmod(g, i * sq * k % (n - 1), n), i};
+  sort(dec.begin(), dec.end());
+  int any_ans = -1;
+  for (int i = 0; i < sq; ++i) {
+    int my = powmod(g, i * k % (n - 1), n) * a % n;
+    auto it = lower_bound(dec.begin(), dec.end(), make_pair(my, 0));
+    if (it != dec.end() && it->first == my) {
+      any_ans = it->second * sq - i;
+      break;
     }
-    if(any_ans==-1) return puts("0"),0;
-    // Print all possible answers
-    int delta=(n-1)/gcd(k, n-1);
-    vector<int> ans;
-    for(int cur=any_ans%delta; cur<n-1; cur+=delta)
-        ans.push_back(powmod(g, cur, n));
-    sort(ans.begin(), ans.end());
-    printf("%d\n", ans.size());
-    for(int answer : ans) printf("%d ", answer);
+  }
+  if (any_ans == -1) return puts("0"), 0;
+  // Print all possible answers
+  int delta = (n - 1) / gcd(k, n - 1);
+  vector<int> ans;
+  for (int cur = any_ans % delta; cur < n - 1; cur += delta)
+    ans.push_back(powmod(g, cur, n));
+  sort(ans.begin(), ans.end());
+  printf("%d\n", ans.size());
+  for (int answer : ans) printf("%d ", answer);
 }
 ```
 
@@ -159,37 +160,37 @@ $$
 
 当 $a\perp p$ 时,在模 $p$ 意义下 $a$ 存在逆元,因此可以使用 BSGS 算法求解。于是我们想办法让他们变得互质。
 
-具体地,设 $d_1=\gcd(a,p)$。如果 $d_1\nmid b$,则原方程无解。否则我们把方程同时除以 $d_1$,得到
+具体地,设 $d_1=\gcd(a,p)$ 。如果 $d_1\nmid b$ ,则原方程无解。否则我们把方程同时除以 $d_1$ ,得到
 
 $$
 \frac{a}{d_1}\cdot a^{x-1}\equiv \frac{b}{d_1}\mod \frac{p}{d_1}
 $$
 
-如果 $a$ 和 $\frac{p}{d_1}$ 仍不互质就再除,设 $d_2=\gcd\left(a,\frac{p}{d_1}\right)$。如果 $d_2\nmid \frac{b}{d_1}$,则方程无解;否则同时除以 $d_2$ 得到
+如果 $a$ 和 $\frac{p}{d_1}$ 仍不互质就再除,设 $d_2=\gcd\left(a,\frac{p}{d_1}\right)$ 。如果 $d_2\nmid \frac{b}{d_1}$ ,则方程无解;否则同时除以 $d_2$ 得到
 
 $$
 \frac{a^2}{d_1d_2}\cdot a^{x-2}≡\frac{b}{d_1d_2} \mod \frac{p}{d_1d_2}
 $$
 
-同理,这样不停的判断下去。直到 $a\perp \frac{p}{d_1d_2\cdots d_k}$。
+同理,这样不停的判断下去。直到 $a\perp \frac{p}{d_1d_2\cdots d_k}$ 
 
-记 $D=\prod_{i=1}^kd_i$,于是方程就变成了这样:
+记 $D=\prod_{i=1}^kd_i$ ,于是方程就变成了这样:
 
 $$
 \frac{a^k}{D}\cdot a^{x-k}\equiv\frac{b}{D} \mod \frac{p}{D}
 $$
 
-由于 $a\perp\frac{p}{D}$,于是推出 $\frac{a^k}{D}\perp \frac{p}{D}$。这样 $\frac{a^k}{D}$ 就有逆元了,于是把它丢到方程右边,这就是一个普通的 BSGS 问题了,于是求解 $x-k$ 后再加上 $k$ 就是原方程的解啦。
+由于 $a\perp\frac{p}{D}$ ,于是推出 $\frac{a^k}{D}\perp \frac{p}{D}$ 。这样 $\frac{a^k}{D}$ 就有逆元了,于是把它丢到方程右边,这就是一个普通的 BSGS 问题了,于是求解 $x-k$ 后再加上 $k$ 就是原方程的解啦。
 
 注意,不排除解小于等于 $k$ 的情况,所以在消因子之前做一下 $\Theta(k)$ 枚举,直接验证 $a^i\equiv b \mod p$ ,这样就能避免这种情况。
 
 ## 习题
 
-- [SPOJ MOD](https://www.spoj.com/problems/MOD/) 模板
-- [SDOI2013 随机数生成器](http://www.lydsy.com/JudgeOnline/problem.php?id=3122)
-- [BZOJ1319 Discrete Roots](http://www.lydsy.com/JudgeOnline/problem.php?id=1319) 模板
-- [SDOI2011 计算器](https://www.luogu.org/problemnew/show/P2485) 模板
-- [Luogu4195 【模板】exBSGS/Spoj3105 Mod](https://www.luogu.org/problemnew/show/P4195) 目标
-- [Codeforces - Lunar New Year and a Recursive Sequence](https://codeforces.com/contest/1106/problem/F)
+-   [SPOJ MOD](https://www.spoj.com/problems/MOD/)模板
+-   [SDOI2013 随机数生成器](http://www.lydsy.com/JudgeOnline/problem.php?id=3122)
+-   [BZOJ1319 Discrete Roots](http://www.lydsy.com/JudgeOnline/problem.php?id=1319)模板
+-   [SDOI2011 计算器](https://www.luogu.org/problemnew/show/P2485)模板
+-   [Luogu4195【模板】exBSGS/Spoj3105 Mod](https://www.luogu.org/problemnew/show/P4195)目标
+-   [Codeforces - Lunar New Year and a Recursive Sequence](https://codeforces.com/contest/1106/problem/F)
 
-**本页面部分内容以及代码译自博文 [Дискретное извлечение корня](http://e-maxx.ru/algo/discrete_root) 与其英文翻译版 [Discrete Root](https://cp-algorithms.com/algebra/discrete-root.html)。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。**
+ **本页面部分内容以及代码译自博文[Дискретное извлечение корня](http://e-maxx.ru/algo/discrete_root)与其英文翻译版[Discrete Root](https://cp-algorithms.com/algebra/discrete-root.html)。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。**