数学知识:欧拉函数,快速幂,扩展欧几里得,线性同余方程 讲解

🎬 博主名称个人主页

🔥 个人专栏 : 《算法通关》《Java讲解》

⛺️心简单,世界就简单

序言

哎数学还挺难的哈,下面讲点数论知识

目录

欧拉函数

利用定义求欧拉函数

利用筛法求欧拉函数

快速幂

快速幂求逆元

裴蜀定理

扩展欧几里得

看数学推导

线性同余方程

欧拉函数

这个意思是 1 ~ n 内与 n 互质的个数

我们来看一下过程

分解n的质因数 :

举个例子 6 = 2 * 3,他的互质个数就是6 * ( 1 - 1 / 2) * ( 1 - 1/ 3) = 6 * 1 / 2 * 2 / 3 = 2

所以与6互质的个数就是2

推导用的是容斥原理,这里就不细说,就直接看上面这个公式就行

利用定义求欧拉函数

cpp 复制代码
#include<iostream>
#include<algorithm>

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);
				//等价于res = res * (1 - 1 / i)  
				while(a % i == 0) a /= i;
			}
		}
			if(a > 1)res = res / a * (a -1);
	
		cout << res << endl;
	}

}

利用筛法求欧拉函数

这个其实不太好推

cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 1000010;

int primes[N], cnt;  // 存储所有质数
int phi[N];         // 存储每个数的欧拉函数值
bool st[N];         // 标记是否是合数,st[i]=true表示i是合数

LL get_eulers(int n){
    phi[1] = 1;  // 规定φ(1)=1
    
    for(int i = 2; i <= n; i++){
        if(!st[i]){  // 如果i是质数
            primes[cnt ++] = i;  // 存储质数
            phi[i] = i - 1;      // 性质1:质数p的φ(p)=p-1
        }
        
        // 用当前已知的质数去筛i的倍数
        for(int j = 0; primes[j] <= n / i; j ++){
            st[primes[j] * i] = true;  // 标记合数
            
            if(i % primes[j] == 0){  // 如果primes[j]是i的质因子
                // 性质2:如果p整除n,则φ(p×n)=p×φ(n)
                phi[primes[j] * i] = primes[j] * phi[i];
                break;  // 保证每个合数只被最小质因子筛
            }
            // 性质3:如果p不整除n,则φ(p×n)=(p-1)×φ(n)
            phi[primes[j] * i] = (primes[j] - 1) * phi[i];
        }
    }
    
    // 计算前缀和
    LL res = 0;
    for(int i = 1; i <= n; i ++) res += phi[i];
    return res;
}

快速幂

先说一下快速幂是来处理什么问题的,他是用来快速求出 a ^ k mod P的结果,在o( log k)的时间复杂度求出结果

这个就比较简单了,其实就是让这个k转化为2进制,求出每一个是1的位的幂,然后我妈有了这些直接进行相加就行

cpp 复制代码
#include<iostream>
#include<algorithm>

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);
				//等价于res = res * (1 - 1 / i)  
				while(a % i == 0) a /= i;
			}
		}
			if(a > 1)res = res / a * (a -1);
	
		cout << res << endl;
	}

}

快速幂求逆元

之前文章也讲过这个,具体可以看之前的文章,我们这里只给个求逆元的模板

cpp 复制代码
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

int n;

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

int main(){
	scanf("%d", &n);
	while(n --){
		int a, p;
		scanf("%d%d", &a, &p);
		
		int res = qmi(a, p - 2, p);
		
		if(a % p) printf("%d\n", res);
		else puts("impossible");
	}
} 

裴蜀定理

有一对正整数a, b,那么存在整数x, 使得ax + by = gcd(a, b)

如果ax + by = d,那么d一定是gcd(a, b)的倍数,所以可以凑出最小的a,b的系数正好是的=最大公约数

扩展欧几里得

看数学推导

我们要解:a × x + b × y = gcd(a, b)

假设我们已经递归计算了下一层:
b × x₁ + (a % b) × y₁ = gcd(b, a % b)

根据欧几里得算法:gcd(a, b) = gcd(b, a % b)

展开 a % b = a - ⌊a/b⌋ × b

复制代码
b × x₁ + (a % b) × y₁ 
= b × x₁ + (a - ⌊a/b⌋ × b) × y₁
= a × y₁ + b × (x₁ - ⌊a/b⌋ × y₁)

比较一下

复制代码
x = y₁
y = x₁ - ⌊a/b⌋ × y₁

所以就得到了下面这个

cpp 复制代码
	int d =  exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
cpp 复制代码
#include<iostream>

using namespace std;

int exgcd(int a, int b, int &x, int &y){
	if(b == 0){
		x = 1, y = 0;
		return a;
	} 
	int d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d; 
}

int main(){
	int n;
	scanf("%d", &n);
	
	while(n --){
		int a, b, x, y;
		scanf("%d%d", &a, &b);
		exgcd(a, b, x, y);
		printf("%d %d\n", x, y);
	}
}

线性同余方程

这个就是转化为扩展欧几里得的形式去求

cpp 复制代码
#include<iostream>

using namespace std;

typedef long long LL;


int exgcd(int a, int b, int &x, int &y){
	if(b == 0){
		x = 1, y = 0;
		return a;
	} 
	int d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d; 
}

int main(){
	int n;
	scanf("%d", &n);
	
	while(n --){
		int a, b, m;
		scanf("%d%d%d", &a, &b, &m);
		int x, y;
		int d = exgcd(a, m, x, y);
		if(b % d) puts("impossible");
		else printf("%d\n", (LL)x * (b / d) % m);
	}
}
相关推荐
寻寻觅觅☆8 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
fpcc8 小时前
并行编程实战——CUDA编程的Parallel Task类型
c++·cuda
偷吃的耗子8 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
l1t8 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
赶路人儿9 小时前
Jsoniter(java版本)使用介绍
java·开发语言
化学在逃硬闯CS9 小时前
Leetcode1382. 将二叉搜索树变平衡
数据结构·算法
ceclar1239 小时前
C++使用format
开发语言·c++·算法
码说AI10 小时前
python快速绘制走势图对比曲线
开发语言·python
Gofarlic_OMS10 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
lanhuazui1010 小时前
C++ 中什么时候用::(作用域解析运算符)
c++