扩展欧拉定理和BSGS算法

扩展欧拉定理

欧拉定理,即:若正整数 a a a m m m 互质,则: a φ ( m ) ≡ 1 (   m o d   m ) a^{\varphi(m)} \equiv 1(\bmod m) aφ(m)1(modm)
这里的 φ ( m ) \varphi(m) φ(m) 是欧拉函数,即小于或等于 m m m 且与 m m m 互质的正整数个数。当 m m m 是质数 p p p 时,欧拉定理退化成 费马小定理 a p − 1 ≡ 1 (   m o d   p ) a^{p-1} \equiv 1(\bmod p) ap11(modp)

接下来我们证明欧拉定理:

记小于 n 且与 n 互质的正整数集合为

R = { x 1 , x 2 , ⋯   , x φ ( n ) } R=\left\{x_{1}, x_{2}, \cdots, x_{\varphi(n)}\right\} R={x1,x2,,xφ(n)}

S = { a x 1 % n , a x 2 % n , ⋯   , a x φ ( n ) % n } S=\left\{a x_{1} \% n, a x_{2} \% n, \cdots, a x_{\varphi(n)} \% n\right\} S={ax1%n,ax2%n,,axφ(n)%n}

∀ i ∈ [ 1 , φ ( n ) ] ∵ ( a , n ) = 1 , ( x i , n ) = 1 ∴ ( a x i , n ) = 1 \begin{array}{l} \forall i \in[1, \varphi(n)] \\ \because(a, n)=1,\left(x_{i}, n\right)=1 \\ \therefore\left(a x_{i}, n\right)=1 \end{array} i[1,φ(n)](a,n)=1,(xi,n)=1(axi,n)=1

由最大公约数的性质可得 ( a x i % n , n ) = 1 \left(a x_{i} \% n, n\right)=1 (axi%n,n)=1
所以 S \mathrm{S} S 中所有元素都与 n n n 互质,且都小于 n n n
S \mathrm{S} S 中无重复元素
假设 i ≠ j , a x i % n = a x j % n i \neq j, a x_{i} \% n=a x_{j} \% n i=j,axi%n=axj%n
a x i ≡ a x j (   m o d   n ) a x_{i} \equiv a x_{j}(\bmod n) axiaxj(modn), 又 ( a , n ) = 1 (a, n)=1 (a,n)=1
∴ x i ≡ x j (   m o d   n ) , i = j \therefore x_{i} \equiv x_{j}(\bmod n), i=j xixj(modn),i=j ,矛盾!
∴ S = R \therefore S=R S=R
∴ ∏ i = 1 φ ( n ) a x i % n = ∏ i = 1 φ ( n ) x i \therefore \prod_{i=1}^{\varphi(n)} a x_{i} \% n=\prod_{i=1}^{\varphi(n)} x_{i} i=1φ(n)axi%n=i=1φ(n)xi
∴ ∏ i = 1 φ ( n ) a x i ≡ ∏ i = 1 φ ( n ) x i (   m o d   n ) ⟹ a φ ( n ) ∏ i = 1 φ ( n ) x i ≡ ∏ i = 1 φ ( n ) x i (   m o d   n ) \therefore \prod_{i=1}^{\varphi(n)} a x_{i} \equiv \prod_{i=1}^{\varphi(n)} x_{i}(\bmod n) \Longrightarrow a^{\varphi(n)} \prod_{i=1}^{\varphi(n)} x_{i} \equiv \prod_{i=1}^{\varphi(n)} x_{i}(\bmod n) i=1φ(n)axii=1φ(n)xi(modn)aφ(n)i=1φ(n)xii=1φ(n)xi(modn)
( ∏ i = 1 φ ( n ) x i , n ) = 1 \left(\prod_{i=1}^{\varphi(n)} x_{i}, n\right)=1 (i=1φ(n)xi,n)=1
a φ ( n ) ≡ 1 (   m o d   n ) a^{\varphi(n)} \equiv 1(\bmod n) aφ(n)1(modn)

在算法竞赛中,我们常常会用到它的一个重要的推论:若正整数 a a a m m m 互质,则 a b ≡ a b   m o d   φ ( m ) (   m o d   m ) a^{b} \equiv a^{b \bmod \varphi(m)}(\bmod m) ababmodφ(m)(modm)
(这是因为 a b = a φ ( m ) ⌊ b / φ ( m ) ⌋ + b   m o d   φ ( m ) ≡ 1 ⋅ a b   m o d   φ ( m ) (   m o d   m ) a^{b}=a^{\varphi(m)\lfloor b / \varphi(m)\rfloor+b \bmod \varphi(m)} \equiv 1 \cdot a^{b \bmod \varphi(m)}(\bmod m) ab=aφ(m)b/φ(m)⌋+bmodφ(m)1abmodφ(m)(modm) )
利用这个推论,即使 b b b 比较大,我们也可以轻松地计算 a b   m o d   m a^{b} \bmod m abmodm 的值,但需要满足 a a a m m m 互质的前提。

为了解决 a a a m m m 不互质时的此问题,我们引入拓展欧拉定理: 若 b ≥ φ ( m ) b \geq \varphi(m) bφ(m) ,则:
a b ≡ a b   m o d   φ ( m ) + φ ( m ) (   m o d   m ) ( ∗ ) a^{b} \equiv a^{b \bmod \varphi(m)+\varphi(m)}(\bmod m) \quad(*) ababmodφ(m)+φ(m)(modm)()
这里仍有前提条件,但影响不大,因为 b < φ ( m ) b<\varphi(m) b<φ(m) 时直接用快速幂计算即可。
当*** b < φ ( m ) b<\varphi(m) b<φ(m)*** 时的板子

#include <bits/stdc++.h>
using namespace std;
bool large_enough = false; // 判断是否有b >= phi(m)
inline int read(int MOD = 1e9 + 7) // 快速读入稍加修改即可以边读入边取模,不取模时直接模一个大于数据范围的数
{
    int ans = 0;
    char c = getchar();
    while (!isdigit(c))
        c = getchar();
    while (isdigit(c))
    {
        ans = ans * 10 + c - '0';
        if (ans >= MOD)
        {
            ans %= MOD;
            large_enough = true;
        }
        c = getchar();
    }
    return ans;
}
int phi(int n) // 求欧拉函数
{
    int res = n;
    for (int i = 2; i * i <= n; i++)
    {
        if (n % i == 0)
            res = res / i * (i - 1);
        while (n % i == 0)
            n /= i;
    }
    if (n > 1)
        res = res / n * (n - 1);
    return res;
}
int qpow(int a, int n, int MOD) // 快速幂
{
    int ans = 1;
    while (n)
    {
        if (n & 1)
            ans = 1LL * ans * a % MOD; // 注意防止溢出
        n >>= 1;
        a = 1LL * a * a % MOD;
    }
    return ans;
}
int main()
{
    int a = read(), m = read(), phiM = phi(m), b = read(phiM);
    cout << qpow(a, b + (large_enough ? phiM : 0), m);
    return 0;
}

BSGS算法

大步小步算法(baby step giant step,BSGS),是一种用来求解离散对数(即模意义下对数)的算法,即给 出 a x ≡ b (   m o d   m ) a^{x} \equiv b(\bmod m) axb(modm) a , b , m a, b, m a,b,m 的值(这里保证 a a a m m m 互质),求解 x x x
既然保证了 a a a m m m 互质,那么很容易联想到欧拉定理,我们知道 a φ ( m ) ≡ 1 (   m o d   m ) a^{\varphi(m)} \equiv 1(\bmod m) aφ(m)1(modm) ,也就说明 a x a^{x} ax 在模 m m m 意义下有一个长度为 φ ( m ) \varphi(m) φ(m) 的循环节。既然后面都是循环的,我们只需要考虑 x ≤ φ ( m ) x \leq \varphi(m) xφ(m) 的情形就可以 了。这就有了离散对数的朴素算法: 暴力枚举。复杂度 O ( φ ( m ) ) O(\varphi(m)) O(φ(m)) ,因为当 m m m 是质数时 φ ( m ) = m − 1 \varphi(m)=m-1 φ(m)=m1 , 最坏时间复杂度就是 O ( m ) O(m) O(m)
实际上,大步小步算法就是对暴力枚举的一个简单的改进,使用了类似meet in the middle的思想(这个算法 还没有讲,待补)。我们把 x x x 拆成 A t − B A t-B AtB ,则原式化为 a A t − B ≡ b (   m o d   m ) a^{A t-B} \equiv b(\bmod m) aAtBb(modm) ,即 a A t ≡ b a B (   m o d   m ) a^{A t} \equiv b a^{B}(\bmod m) aAtbaB(modm) 。然后我们预计算出右侧所有可能的取值,再固定一个 t t t ,计算出左边可能的值,当 发现某个值已经在右边出现过,这时的 A t − B A t-B AtB 就是我们要求的 x x x

B可能的取值有 φ ( m )   m o d   t \varphi(m) \bmod t φ(m)modt 个,A可能的取值有 ⌊ φ ( m ) / t ⌋ \lfloor\varphi(m) / t\rfloor φ(m)/t 个,不难看出,取 t = ⌈ φ ( m ) ] t=\lceil\sqrt{\varphi(m)}] t=φ(m) ] 是最好 的,当然为了避免计算欧拉函数,我们直接取 t = ⌈ m ⌉ t=\lceil\sqrt{m}\rceil t=m ,不难验证,此时取 A , B ∈ [ 1 , t ] A, B \in[1, t] A,B[1,t] 可以保证把 x ∈ [ 1 , m − 1 ] x \in[1, m-1] x[1,m1] 全部枚举一遍。时间复杂度为 O ( m ) O(\sqrt{m}) O(m )
至于如何判定右边的数有没有出现过,用哈希表是最简单的(STL中的 unordered_map 是用哈希表实现的,一般 可以直接使用),如果用 map,复杂度会多一个 log ⁡ \log log

ll BSGS(ll a, ll b, ll m)
{
    static unordered_map<ll, ll> hs;
    hs.clear();
    ll cur = 1, t = sqrt(m) + 1;
    for (int B = 1; B <= t; ++B)
    {
        (cur *= a) %= m;
        hs[b * cur % m] = B; // 哈希表中存B的值
    }
    ll now = cur; // 此时cur = a^t
    for (int A = 1; A <= t; ++A)
    {
        auto it = hs.find(now);
        if (it != hs.end())
            return A * t - it->second;
        (now *= cur) %= m;
    }
    return -1; // 没有找到,无解
}

拓展大步小步算法

上面的算法只针对 a a a m m m 互质的情形,如果不互质,又该如何计算? 我们尝试把它转化成 a a a m m m 互质的情 形。
x > 0 x>0 x>0 时, a x ≡ b (   m o d   m ) a^{x} \equiv b(\bmod m) axb(modm) 等价于 a x − 1 a + n m = b a^{x-1} a+n m=b ax1a+nm=b ,由斐蜀定理(此处曾提到过)可知,有解的 条件是 b b b gcd ⁡ ( a , m ) \operatorname{gcd}(a, m) gcd(a,m) 的倍数。
d = gcd ⁡ ( a , m ) d=\operatorname{gcd}(a, m) d=gcd(a,m) ,则 a x − 1 a d + n m d = b d \frac{a^{x-1} a}{d}+\frac{n m}{d}=\frac{b}{d} dax1a+dnm=db ,即 a d a x − 1 ≡ b d (   m o d   m d ) \frac{a}{d} a^{x-1} \equiv \frac{b}{d}\left(\bmod \frac{m}{d}\right) daax1db(moddm) 。这时如果 gcd ⁡ ( a , m d ) = 1 \operatorname{gcd}\left(a, \frac{m}{d}\right)=1 gcd(a,dm)=1 , 就可以直接用大步小步算法求解了 (左侧多了一个系数,但这显然影响不大,把大步小步算法稍作修改即可), 解出的 x x x 加上 1 即是原方程的一个解。否则,可以继续递归下去,直到互质为止。
但是,这样算出来的只保证是原方程的一个解,末必是最小解,因为虽然BSGS求出的是最小解,但我们还进行 了递归层数 (下面称为 c n t c n t cnt ) 次加 1 ,真正的最小值可能在 [ 0 , c n t ) [0, c n t) [0,cnt) 间,所以还需要枚举这些数是否为答案。
注意一下特判,本来BSGS是不需要特判的,但是我们的递归条件只对 x > 0 x>0 x>0 时成立。所以如果 b ≡ 1 b \equiv 1 b1 直接返 回 x = 0 x=0 x=0 即可。
为了进一步加快速度,我们用非递归而非递归的方式实现这个算法。这里很容易做到,而且写起来比递归版本更 加简洁。

// 修改版的BSGS,额外带一个系数
ll BSGS(ll a, ll b, ll m, ll k = 1)
{
    static unordered_map<ll, ll> hs;
    hs.clear();
    ll cur = 1, t = sqrt(m) + 1;
    for (int B = 1; B <= t; ++B)
    {
        (cur *= a) %= m;
        hs[b * cur % m] = B; // 哈希表中存B的值
    }
    ll now = cur * k % m;
    for (int A = 1; A <= t; ++A)
    {
        auto it = hs.find(now);
        if (it != hs.end()) return A * t - it->second;
        (now *= cur) %= m;
    }
    return -INF; // 这里因为要多次加1,要返回更小的负数
}
ll exBSGS(ll a, ll b, ll m, ll k = 1)
{
    ll A = a %= m, B = b %= m, M = m;
    if (b == 1) return 0;
    ll cur = 1 % m;
    for (int i = 0;; i++)
    {
        if (cur == B) return i;
        cur = cur * A % M;
        ll d = gcd(a, m);
        if (b % d) return -INF;
        if (d == 1) return BSGS(a, b, m, k * a % m) + i + 1;
        k = k * a / d % m, b /= d, m /= d; // 相当于在递归求解exBSGS(a, b / d, m / d, k * a / d % m)
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值