【数学】欧拉定理

验证链接:https://www.luogu.com.cn/problem/P5091

namespace exET {

    int calcA(char *a, int mod) {
        ll res = 0;
        for(int i = 1, n = strlen(a + 1); i <= n; ++i) {
            res = res * 10 + (a[i] - '0');
            if(res >= mod)
                res %= mod;
        }
        return res;
    }

    int calcB(char *b, int mod) {
        ll res = 0, flag = 0;
        for(int i = 1, n = strlen(b + 1); i <= n; ++i) {
            res = res * 10 + (b[i] - '0');
            if(res >= mod) {
                res %= mod;
                flag = 1;
            }
        }
        if(flag == 1)
            res += mod;
        return res;
    }

    ll qpow(ll a, ll n, ll mod) {
        ll res = 1;
        while(n) {
            if(n & 1)
                res = res * a % mod;
            a = a * a % mod;
            n >>= 1;
        }
        return res;
    }

    ll exet(char *A, char *B, int m) {
        int phim = phi(m);
        int a = calcA(A, m);
        int b = calcB(B, phim);
        int res = qpow(a, b, m);
        return res;
    }

}

其中,计算欧拉函数的部分如下:

const int MAXN = 1e6 + 10;
int p[MAXN], ptop;
int pm[MAXN];

void sieve(int n) {
    pm[1] = 1;
    for(int i = 2; i <= n; ++i) {
        if(!pm[i])
            p[++ptop] = i, pm[i] = i;
        for(int j = 1; j <= ptop; ++j) {
            int t = i * p[j];
            if(t > n)
                break;
            pm[t] = p[j];
            if(i % p[j])
                ;
            else
                break;
        }
    }
}

int phi(int n) {
    int res = n;
    for(int i = 1; p[i]*p[i] <= n; ++i) {
        if(n % p[i] == 0) {
            res = res / p[i] * (p[i] - 1);
            while(n % p[i] == 0)
                n /= p[i];
        }
    }
    if(n > 1)
        res = res / n * (n - 1);
    return res;
}

欧拉定理:若 \(gcd(a,m)=1\) ,则 \(a^{\varphi(m)}\equiv1 (\mod m)\)
扩展欧拉定理:若 \(gcd(a,m) \neq 1\) ,当 \(b<\varphi(m)\) 则直接求解 \(a^{b}\mod m)\) ,当 \(b\geq\varphi(m)\)\(a^{b}\equiv a^{b\mod \varphi(m) + \varphi(m)}(\mod m)\)

注意其实是 \(a^r\equiv a^{r+s}(\mod m)\) ,r是循环节的起点,s是循环节的长度,证明可以用鸽巢原理来证明。

\(a=2,m=6\) 时,观察下面的序列:

         b | 0  1  2  3  4  5  6
       a^b | 1  2  4  8 16 32 64
a^b \mod m | 1  2  4  2  4  2  4

容易看出 \(a^0 \neq a^2\) ,故 \(a^{b}\not\equiv a^{b\mod \varphi(m) + \varphi(m)}(\mod m)\) ,扩展欧拉定理生效必须要先超过循环的起点。

算法流程:

  1. 输入 \(m\) ,并计算 \(m\) 的欧拉函数 \(\varphi(m)\)
  2. 输入 \(a\) ,同时令 \(a\)\(m\) 取模
  3. 输入 \(b\) ,同时令 \(b\)\(\varphi(m)\) 取模,若 \(\varphi(m)=m-1\) 且最后取了至少1次模,则令 \(b:=b+\varphi(m)\)
  4. 快速幂计算 \(a^b \mod m\)

算法复杂度为 \(O(\sqrt{m}+\log{a}+\log{b})\)

posted @ 2021-01-22 11:52  purinliang  阅读(305)  评论(0编辑  收藏  举报