数学知识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;
}
本文来自博客园,作者:Cocoicobird,转载请注明原文链接:https://www.cnblogs.com/Cocoicobird/p/16750625.html
浙公网安备 33010602011771号