浅谈欧拉定理及其扩展

欧拉定理

​ 欧拉定理其实是费马小定理的一个推广,首先来回顾一下费马小定理。

费马小定理

​ p为质数,且a不是p的倍数,则有
a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \quad (mod \quad p) ap11(modp)

欧拉定理

​ 设p是任意大于1的整数,且(a, m)=1,则有
a φ ( p ) ≡ 1 ( m o d p ) a^{\varphi(p)}\equiv 1 \quad (mod \quad p) aφ(p)1(modp)
φ \varphi φ§指的是从1到p-1中,与p互素的元素的个数。

​ 举个例子来深度理解一下欧拉定理,假如p = 20, a = 3,可知a与p互素,则有

a 0 ≡ 1 a 1 ≡ 3 a 2 ≡ 9 a 3 ≡ 7 a 4 ≡ 1 a^{0} \equiv 1 \\a^{1} \equiv 3 \\a^{2} \equiv 9 \\a^{3} \equiv 7 \\a^{4} \equiv 1 a01a13a29a37a41
不难发现当取到 a 4 a^{4} a4时再次出现了1的情况,因此以后都是以4为一个循环,那再假如p = 20,a = 2,则有
a 0 ≡ 1 a 1 ≡ 2 a 2 ≡ 4 a 3 ≡ 8 a 4 ≡ 16 a 5 ≡ 12 a 6 ≡ 4 a^{0} \equiv 1 \\a^{1} \equiv 2 \\a^{2} \equiv 4 \\a^{3} \equiv 8 \\a^{4} \equiv 16 \\a^{5} \equiv 12 \\a^{6} \equiv 4 a01a12a24a38a416a512a64
此时,发现 a 2 ≡ a 6 a^{2} \equiv a^{6} a2a6显然,循环不是从第一个元素开始,是先经过一段不循环的数,然后再进入到循环当中。

综上,可以得出结论,当 ( a , p ) = 1 (a, p)=1 (a,p)=1时,从一开始就进入了循环当中,当 ( a , p ) ≠ 1 (a, p)\neq 1 (a,p)=1时,是先经过一段不循环的数,然后再进入到循环当中。

扩展欧拉定理

扩展欧拉定理一般用来欧拉降幂

先给出公式:
a m ≡ { a m , if  m < φ ( p ) a m % φ ( p ) + φ ( p ) , if  m ≥ φ ( p ) a^{m} \equiv \begin{cases} a^{m},& \text{if $m < \varphi(p)$}\\ a^{m \% \varphi(p) + \varphi(p)},& \text{if $m \geq \varphi(p)$} \end{cases} am{am,am%φ(p)+φ(p),if m<φ(p)if mφ(p)
m < φ ( p ) m<\varphi(p) m<φ(p)时,说明m只在第一个循环或者甚至不在循环当中,直接快速幂求解即可。

m ≥ φ ( p ) m \geq \varphi(p) mφ(p)时,说明m已经在循环当中,需要确定的就是他在循环当中的哪个数,a的幂次是 m % φ ( p ) + φ ( p ) m\% \varphi(p)+\varphi(p) m%φ(p)+φ(p)实际上是化简后的 ( m − φ ( p ) ) % φ ( p ) + φ ( p ) (m-\varphi(p))\%\varphi(p)+\varphi(p) (mφ(p))%φ(p)+φ(p),加上一个 φ ( p ) \varphi(p) φ(p)就是为了让其先进入循环中,然后再去确定他在循环当中的哪个数。

模板题

Notepad

题意易懂,因为没有前导0,所以第一位只有(b-1)种情况,所以答案其实就是 a n s ≡ ( b − 1 ) ∗ b n − 1 m o d      c ans\equiv (b-1)*b^{n-1}\mod\ c ans(b1)bn1mod c,因为b和n的位数特别多,需要高精度减法预处理出(b - 1)和(n - 1),就可以得到
a n s ≡ { ( ( b − 1 ) % c ) ∗ ( b % c ) n − 1 , if  n − 1 < φ ( c ) ( ( b − 1 ) % c ) ∗ ( b % c ) ( n − 1 ) % φ ( c ) + φ ( c ) , if  ( n − 1 ) ≥ φ ( c ) ans \equiv \begin{cases} ((b - 1)\% c) * (b \% c)^{n - 1},& \text{if $n - 1 < \varphi(c)$}\\ ((b - 1)\% c) * (b \% c)^{(n - 1) \% \varphi(c) + \varphi(c)},& \text{if $(n - 1) \geq \varphi(c)$} \end{cases} ans{((b1)%c)(b%c)n1,((b1)%c)(b%c)(n1)%φ(c)+φ(c),if n1<φ(c)if (n1)φ(c)
答案就显而易见了。

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
LL qp(LL a, LL b, LL mod) {
    LL res = 1;
    for (; b; b >>= 1, a = a * a % mod) {
        if (b & 1) {
            res = res * a % mod;
        }
    }
    return res;
}
LL phi(LL p) {
    if (p == 1) {
        return 0;
    }
    LL phip = p, q = p;
    for (int i = 2; i * i <= q; i++) {
        if (q % i == 0) {
            phip = phip / i * (i - 1);
            while (q % i == 0) {
                q /= i;
            }
        }
    }
    if (q != 1) {
        phip = phip / q * (q - 1);
    }
    return phip;
}
string calc(string x) {
    int len = x.size();
    string ans = "";
    vector<int> z;
    for (int i = 0; i < len; i++) {
        z.emplace_back(x[i] - '0');
    }
    z[len - 1]--;
    for (int i = len - 1; i >= 0; i--) {
        if (z[i] < 0) {
            z[i - 1]--;
            z[i] += 10;
        }
    }
    int first = 0;
    while (z[first] == 0) {
        first++;
    }
    for (int i = first; i < len; i++) {
        ans += ('0' + z[i]);
    }
    return ans;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string b, n;
    LL c;
    cin >> b >> n >> c;
    if (c == 1) {
        cout << "1\n";
        return 0;
    }
    LL phic = phi(c);
    int len1 = b.size(), len2 = n.size();
    string bb = calc(b), nn = calc(n);
    LL b1 = 0, bb1 = 0, c1 = 0;
    int len3 = bb.size();
    for (int i = 0; i < len3; i++) {
        b1 = (b1 * 10 + (bb[i] - '0')) % c;
    }
    for (int i = 0; i < len1; i++) {
        bb1 = (bb1 * 10 + (b[i] - '0')) % c;
    }
    int len4 = nn.size();
    bool ok = true;
    for (int i = 0; i < len4; i++) {
        c1 = (c1 * 10 + (nn[i] - '0'));
        if (c1 >= phic) {
            ok = false;
            break;
        }
    }
    if (ok) {
        c1 = 0;
        for (int i = 0; i < len4; i++) {
            c1 = (c1 * 10 + (nn[i] - '0'));
        }
        LL ans = b1 * qp(bb1, c1, c) % c;
        if (ans == 0) {
            ans = c;
        }
        cout << ans << '\n';
    } else {
        c1 = 0;
        for (int i = 0; i < len4; i++) {
            c1 = (c1 * 10 + (nn[i] - '0')) % phic;
        }
        LL ans = b1 * qp(bb1, c1 + phic, c) % c;
        if (ans == 0) {
            ans = c;
        }
        cout << ans << '\n';
    }
    return 0;
}

Exponial

欧拉降幂模板题,只需要求出
n ( n − 1 ) ( n − 2 ) ⋯ 2 1 m o d      m n^{(n - 1)^{(n - 2)^{\cdots^{2^{1}}}}}\mod\ m n(n1)(n2)21mod m
像这种幂次特别多的,分解一下就是要分别求
( n − 1 ) ( n − 2 ) ⋯ 2 1 m o d      φ ( m ) + φ ( m ) ( n − 2 ) ( n − 3 ) ⋯ 2 1 m o d      φ ( φ ( m ) ) + φ ( φ ( m ) ) ⋯ (n - 1)^{(n - 2) \cdots^{2^{1}}}\mod\ \varphi(m) + \varphi(m)\\ (n - 2)^{(n - 3) \cdots^{2^{1}}}\mod\ \varphi(\varphi(m)) + \varphi(\varphi(m))\\ \cdots (n1)(n2)21mod φ(m)+φ(m)(n2)(n3)21mod φ(φ(m))+φ(φ(m))
因为 φ ( m ) \varphi(m) φ(m)经过logm次大概就成为1了,所以这个递归只需要logm次左右,并且要确保 x ≥ φ ( m ) x \geq \varphi(m) xφ(m),x是幂次,来保证扩展欧拉定理的运行,因为 m ≤ 1 0 9 m \leq 10^{9} m109,所以当 n ≥ 5 n \geq 5 n5时均符合扩展欧拉定理的要求,当 n < 5 n<5 n<5时直接返回模m后的值,因为值都是在可以暴力计算的范围内。

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int long long
LL qp(LL a, LL b, LL mod) {
    LL res = 1;
    for (; b; b >>= 1, a = a * a % mod) {
        if (b & 1) {
            res = res * a % mod;
        }
    }
    return res;
}
LL solve(LL n, LL m) {
    if (n == 1) {
        return 1 % m;
    }
    if (n == 2) {
        return 2 % m;
    }
    if (n == 3) {
        return 9 % m;
    }
    if (n == 4) {
        return (1 << 18) % m;
    }
    if (m == 1) {
        return 0;
    }
    if (n == 0) {
        return 1;
    }
    LL phim = m, q = m;
    for (int i = 2; i * i <= q; i++) {
        if (q % i == 0) {
            phim = phim / i * (i - 1);
            while (q % i == 0) {
                q /= i;
            }
        }
    }
    if (q != 1) {
        phim = phim / q * (q - 1);
    }
    return qp(n, solve(n - 1, phim) + phim, m);
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    LL n, m;
    cin >> n >> m;
    cout << solve(n, m) % m << '\n';
    return 0;
}

再附上几个练习题

Power Tower

上帝与集合的正确用法

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值