数学知识2.1-欧拉函数与快速幂

一、简述

本文章主要介绍欧拉函数以及快速幂的相关算法。

二、欧拉函数

定义

\(1∼N\) 中与 \(N\) 互质的数的个数被称为欧拉函数,记为 \(\phi(N)\)。若在算数基本定理中,\(N=p^{a1}_1p^{a2}_2…p^{am}_m\),则:\(\phi(N)=N\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}\)

计算式的证明

情况1

\(n=0\) 时,显然,因为 \(n=0\) 时,欧拉函数的计算范围内只有 \(0\),但是 \(\phi(0)=0\)
\(n=1\) 时,因为 \(1\) 与自身互素,\(\phi(1)=1\)

情况2

\(n\) 为素数时,因为 \(1∼n\) 都不被 \(n\) 整除,所以这些数都与 \(n\) 互素,\(\phi(n)=n-1\)

情况3

\(n\) 进行质因数分解 \(n=p^{a1}_1p^{a2}_2…p^{am}_m\)
\(1∼n\) 中所有 \(p_1,p_2...p_m\) 的倍数去掉,剩余 \(n-(n\sum_\limits{i=1}^m\frac{1}{p_i})\) 个数,由于该过程 \(1∼n\) 中既是 \(p_i\) 又是 \(p_j(i\neq j)\) 倍数的数被重复去除,所以我们需要将这些数加回来,即 \(n-(n\sum_\limits{i=1}^m\frac{1}{p_i})+(n\sum_\limits{j,k=1,j\neq k}^m\frac{1}{p_jp_k})\),以此类推,我们会发现这是多集合容斥计算式,总结为 \(\phi(N)=N\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}\)

模板题AcWing873.欧拉函数

题目描述

给定 \(n\) 个正整数 \(a_i\),请你求出每个数的欧拉函数。

输入格式

第一行包含整数 \(n\)
接下来 \(n\) 行,每行包含一个正整数 \(a_i\)

输出格式

输出共 \(n\) 行,每行输出一个正整数 \(a_i\) 的欧拉函数。

数据范围

\(1≤n≤100,\)
\(1≤a_i≤2×10^9\)

输入样例
3
3
6
8
输出样例
2
2
4
解题思路

根据欧拉函数的计算式计算即可,时间复杂度\(O(n\sqrt{a})\)

C++代码
#include <iostream>
using namespace std;

int main()
{
    int n;
    cin >> n;
    while(n --)
    {
        int a;
        cin >> a;
        int res = a;
        for(int i = 2; i <= a / i; i ++)
        {
            if(a % i == 0)
            {
                res = res / i * (i - 1);
                while(a % i == 0)
                    a /= i;
            }
        }
        if(a > 1) res = res / a * (a - 1);//先除后乘可以避免数值溢出
        cout << res << endl;
    }
    return 0;
}

三、筛法求欧拉函数

思路

借用线性筛进行欧拉函数的计算

模板题AcWing874.筛法求欧拉函数

题目描述

给定一个正整数 \(n\),求 \(1∼n\) 中每个数的欧拉函数之和。

输入格式

共一行,包含一个整数 \(n\)

输出格式

共一行,包含一个整数,表示 \(1∼n\) 中每个数的欧拉函数之和。

数据范围

\(1≤n≤10^6\)

输入样例
6
输出样例
12
解题思路

由于数据范围为 \(10^6\),显然我们不能根据定义去依次计算再求解。我们考虑使用线性筛法的方式区计算,具体思路借助代码解释。
关于代码中 \(flag1\):根据欧拉函数计算式可知 \(i=p^{a1}_1p^{a2}_2…p^{am}_m\)\(\phi(i)=i\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}\);而 \(t=primes[j]\times i\),而 \(primes[j]\) 整除 \(i\),故 \(t=p^{a1}_1p^{a2}_2…p^{am}_m\),除了 \(t\)\(primes[j]\) 的指数比 \(i\) 多一,则 \(\phi(t)=t\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}=primes[j]\times i\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}=primes[j]\times\phi(i)\)
关于代码中 \(flag2\)\(i\)\(primes[j]\) 互质。则 \(t\) 的质因数分解会比 \(i\) 多一个质因数。\(\phi(i)=i\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}\)\(\phi(t)=t\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}\times\frac{primes[j]-1}{primes[j]}=primes[j]\times i\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}\times\frac{primes[j]-1}{primes[j]}=\phi(i)\times(primes[j]-1)\)

C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long LL;

int n;
int primes[N], cnt;
int euler[N];
bool st[N];

void get_eulers(int n)
{
    euler[1] = 1;//1对应的欧拉函数为1
    for(int i = 2; i <= n; i ++)
    {
        if(!st[i])//i为质数
        {
            primes[cnt ++] = i;//筛出质数
            euler[i] = i - 1;//质数对应的欧拉函数值为本身减1
        }
        for(int j = 0; primes[j] <= n / i; j ++)
        {
            int t = primes[j] * i;
            st[t] = true;
            if(i % primes[j] == 0)//primes[j]为i的一个质因数,同时也是t的质因数
            {
		//flag1
		euler[t] = euler[i] * primes[j];
                break;
            }
	    //flag2
            euler[t] = euler[i] * (primes[j] - 1);
        }
    }
}

int main()
{
    cin >> n;
    get_eulers(n);
    LL res = 0;
    for (int i = 1; i <= n; i ++ ) res += euler[i];
    cout << res << endl;
    return 0;
}

四、快速幂

正常计算一个数 \(a\)\(k\) 次方,则时间复杂度为 \(O(k)\)。而我们换种思路,计算 \(a\)\(k\) 次方,我们可以使用 \(a\)\(\frac{k}{2}\) 自乘计算。那我们就有一种思路 \(k\) 是偶数 \(a^k=a^\frac{k}{2}\times a^\frac{k}{2}\)\(k\) 是奇数 \(a^k=a^{k-1}\times a\),特别的 \(k=0\)\(a^k=1\)。这是一种递归的快速幂。那么我们考虑一下将 \(k\) 拆解为二进制形式,从低位向高位进行计算,每次移位 \(a\) 都要自乘,且如果当前为 \(1\),就要与当前的 \(a\) 相乘。

模板题AcWing875.快速幂

题目描述

给定 \(n\)\(a_i,b_i,p_i\),对于每组数据,求出 \(a^{b_i}_i mod p_i\) 的值。

输入格式

第一行包含整数 \(n\)
接下来 \(n\) 行,每行包含三个整数 \(a_i,b_i,p_i\)

输出格式

对于每组数据,输出一个结果,表示 \(a^{b_i}_i mod p_i\) 的值。
每个结果占一行。

数据范围

\(1≤n≤100000,\)
\(1≤a_i,b_i,p_i≤2×10^9\)

输入样例
2
3 2 5
4 3 9
输出样例
4
1
C++代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

LL qmi(int a, int b, int p)
{
    LL res = 1;
    while(b)
    {
        if(b & 1) res = res * a % p;
        a = (LL) a * a % p;
        b >>= 1;
    }
    return res;
}

int n;

int main()
{
    cin >> n;
    while(n --)
    {
        int a, b, p;
        cin >> a >> b >> p;
        cout << qmi(a, b, p) << endl;
    }
    return 0;
}

四、乘法逆元

若整数 \(b,m\) 互质,并且对于任意的整数 \(a\),如果满足 \(b|a\),则存在一个整数 \(x\),使得\(\frac{a}{b}\equiv a\times x(mod\,m)\),则称 \(x\)\(b\) 的模 \(m\) 乘法逆元,记为\(b^{−1}(mod\,m)\)
\(b\) 存在乘法逆元的充要条件是 \(b\) 与模数 \(m\) 互质。当模数 \(m\) 为质数时,\(b^{m−2}\) 即为 \(b\) 的乘法逆元(该求法由下面的欧拉定理给出)。

欧拉定理

\(a\)\(n\) 互质,则 \(a^{\phi(n)}\equiv 1(mod\,n)\)。特别地,\(n\) 为质数时 \(\phi(n)=n-1\),故 \(a^{n-1}\equiv 1(mod\,n)\)
证明:已知 \(a\)\(n\) 互质。在 \(1~n\) 中,有 \(\phi(n)\) 个数与 \(n\) 互质,记作 \(b_1,b_2,...,b_{\phi(n)}\)。同样的,\(a·b_1,a·b_2,...,a·b_{\phi(n)}\)\(n\) 互质。那么可以得到 \(b_1·b_2·...·b_{\phi(n)}{\equiv}a^{\phi(n)}·(b_1·b_2·...·b_{\phi(n)}) (mod\,n)\),则 \(a^{\phi(n)}\equiv 1(mod\,n)\)

模板题AcWing876.快速幂求逆元

题目描述

给定 \(n\)\(a_i,p_i\),其中 \(p_i\) 是质数,求 \(a_i\)\(p_i\) 的乘法逆元,若逆元不存在则输出 impossible
注意:请返回在 \(0∼p−1\) 之间的逆元。

输入格式

第一行包含整数 \(n\)
接下来 \(n\) 行,每行包含一个数组 \(a_i,p_i\),数据保证 \(p_i\) 是质数。

输出格式

输出共 \(n\) 行,每组数据输出一个结果,每个结果占一行。
\(a_i\)\(p_i\) 的乘法逆元存在,则输出一个整数,表示逆元,否则输出 impossible

数据范围

\(1≤n≤10^5,\)
\(1≤a_i,p_i≤2\times 10^9\)

输入样例
3
4 3
8 5
6 3
输出样例
1
2
impossible
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long LL;

LL qmi(int a, int k, int p)
{
    LL res = 1;
    while(k)
    {
        if(k & 1) res = res * a % p;
        a = (LL) a * a % p;
        k >>= 1;
    }
    return res;
}

int n;

int main()
{
    cin >> n;
    while(n --)
    {
        int a, p;
        cin >> a >> p;
        if(a % p == 0) puts("impossible");
        else printf("%lld\n", qmi(a, p - 2, p));
    }
    return 0;
}
posted @ 2022-10-03 15:50  Cocoicobird  阅读(124)  评论(0)    收藏  举报