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

一、简述

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

二、欧拉函数

定义

1N 中与 N 互质的数的个数被称为欧拉函数,记为 ϕ(N)。若在算数基本定理中,N=p1a1p2a2pmam,则:ϕ(N)=N×p11p1×p21p2××pm1pm

计算式的证明

情况1

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

情况2

n 为素数时,因为 1n 都不被 n 整除,所以这些数都与 n 互素,ϕ(n)=n1

情况3

n 进行质因数分解 n=p1a1p2a2pmam
1n 中所有 p1,p2...pm 的倍数去掉,剩余 n(ni=1m1pi) 个数,由于该过程 1n 中既是 pi 又是 pj(ij) 倍数的数被重复去除,所以我们需要将这些数加回来,即 n(ni=1m1pi)+(nj,k=1,jkm1pjpk),以此类推,我们会发现这是多集合容斥计算式,总结为 ϕ(N)=N×p11p1×p21p2××pm1pm

模板题AcWing873.欧拉函数

题目描述

给定 n 个正整数 ai,请你求出每个数的欧拉函数。

输入格式

第一行包含整数 n
接下来 n 行,每行包含一个正整数 ai

输出格式

输出共 n 行,每行输出一个正整数 ai 的欧拉函数。

数据范围

1n100,
1ai2×109

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

根据欧拉函数的计算式计算即可,时间复杂度O(na)

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,求 1n 中每个数的欧拉函数之和。

输入格式

共一行,包含一个整数 n

输出格式

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

数据范围

1n106

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

由于数据范围为 106,显然我们不能根据定义去依次计算再求解。我们考虑使用线性筛法的方式区计算,具体思路借助代码解释。
关于代码中 flag1:根据欧拉函数计算式可知 i=p1a1p2a2pmamϕ(i)=i×p11p1×p21p2××pm1pm;而 t=primes[j]×i,而 primes[j] 整除 i,故 t=p1a1p2a2pmam,除了 tprimes[j] 的指数比 i 多一,则 ϕ(t)=t×p11p1×p21p2××pm1pm=primes[j]×i×p11p1×p21p2××pm1pm=primes[j]×ϕ(i)
关于代码中 flag2iprimes[j] 互质。则 t 的质因数分解会比 i 多一个质因数。ϕ(i)=i×p11p1×p21p2××pm1pmϕ(t)=t×p11p1×p21p2××pm1pm×primes[j]1primes[j]=primes[j]×i×p11p1×p21p2××pm1pm×primes[j]1primes[j]=ϕ(i)×(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;
}

四、快速幂

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

模板题AcWing875.快速幂

题目描述

给定 nai,bi,pi,对于每组数据,求出 aibimodpi 的值。

输入格式

第一行包含整数 n
接下来 n 行,每行包含三个整数 ai,bi,pi

输出格式

对于每组数据,输出一个结果,表示 aibimodpi 的值。
每个结果占一行。

数据范围

1n100000,
1ai,bi,pi2×109

输入样例
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,使得aba×x(modm),则称 xb 的模 m 乘法逆元,记为b1(modm)
b 存在乘法逆元的充要条件是 b 与模数 m 互质。当模数 m 为质数时,bm2 即为 b 的乘法逆元(该求法由下面的欧拉定理给出)。

欧拉定理

an 互质,则 aϕ(n)1(modn)。特别地,n 为质数时 ϕ(n)=n1,故 an11(modn)
证明:已知 an 互质。在 1 n 中,有 ϕ(n) 个数与 n 互质,记作 b1,b2,...,bϕ(n)。同样的,a·b1,a·b2,...,a·bϕ(n)n 互质。那么可以得到 b1·b2·...·bϕ(n)aϕ(n)·(b1·b2·...·bϕ(n))(modn),则 aϕ(n)1(modn)

模板题AcWing876.快速幂求逆元

题目描述

给定 nai,pi,其中 pi 是质数,求 aipi 的乘法逆元,若逆元不存在则输出 impossible
注意:请返回在 0p1 之间的逆元。

输入格式

第一行包含整数 n
接下来 n 行,每行包含一个数组 ai,pi,数据保证 pi 是质数。

输出格式

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

数据范围

1n105,
1ai,pi2×109

输入样例
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 @   Cocoicobird  阅读(86)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示