学习笔记——数论
写在前面...
写完了数论的笔记,新知识不确定有没有学懂,但是我的md数学公式写法得到了极大的提升orz
1. 埃式筛法
质数的定义:
针对从2开始的整数定义,如果只包含1和本身这两个因数,则称该数为质数(素数)
(1)质数的判定:试除法
枚举因数的时候,只枚举到因数比较小的那个范围(根号n)
(2)分解质因数:试除法
从小到大枚举所有的数,对于质因子,可通过循环求出每个质因子的个数,从小往大除可以保证其因子一定是质数。
且
朴素筛法时间复杂度为
定义及其原理
每个合数都可以表示为多个质数的积,因此从最小的质数2开始,用每一个已找到的质数去筛选比它大的数,从而筛掉合数,剩下的就是素数。
算法过程
1)从最小的质数2开始,遍历
2)对于当前数
3)不断重复过程2,直到遍历到
时间复杂度
2. 线性筛法
原理及算法过程
n只会被最小质因子筛掉,且合数一定会被筛掉
1)
2)
const int N = 10000010;
int primes[N], cnt;
bool st[N];
void get_primes(int n){
for(int i = 2; i <= n; i++){
if(!st[i]) primes[cnt ++] = i;
//j从小到大枚举所有质数
for(int j = 0; primes[j] <= n / i; j++){
st[primes[j] * i] = true;
//当该条件成立的时候, prime[j]一定是i的最小质因子
if(i % primes[j] == 0) break;
}
}
}
质数定理
3. 约数
1)试除法求一个数的所有约数
从小到大枚举所有可能的数字,时间复杂度为
2) 求约数个数
当
对于一个数的所有约数,一定可以由上面的通式来表示,且有所对应的
约数用M表示,那么
对于
所以对于N而言,其对应的约数个数为
计算时间复杂度
对于i而言,n以内所有倍数的个数表示为
所以n以内所有数的倍数个数表示为
3)求约数之和(没懂)
根据乘法原理和加法原理,约数之和可以表示为:
#include<stdio.h>
#include<iostream>
#include<unordered_map>
using namespace std;
typedef long long ll;
//unordered_map是个好东西
unordered_map<ll, ll> primes;
ll MOD = 1e9+7;
void cnt_y(ll x){
for (int i = 2; i <= x / i; i ++ )
while (x % i == 0){
x /= i;
primes[i] ++;
}
//如果有大于根号x的质因子
if (x > 1) primes[x] ++;
}
int main(){
ll n, a, ans = 1;
cin >> n;
for(ll i = 1; i <= n; i++){
cin >> a;
cnt_y(a);
}
for(auto p : primes){
ll t = 1;
//a是质因子值,b是质因子对应的指数
ll a = p.first, b = p.second;
for(ll j = 1; j <= b; j++)
t = (t * a + 1) % MOD;
ans = ans * t % MOD;
}
cout << ans << endl;
return 0;
}
4)欧几里得算法(辗转相除法)
求两数的最大公约数
通过原理:如果a能整除d,且b能整除d,那么ax+by也能够整除d,可推:
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
4. 欧拉函数
若
那么
其时间复杂度为
该公式的证明需要用到容斥原理
方法:
从1~n中删掉所有
加上所有
减去所有
一直重复上述过程,偶数因子数要加上,奇数因子数要减掉,直到覆盖掉所有的因子数。这个公式最后就可以由上面
代码实现
int n;
cin >> n;
while(n--){
int a;
cin >> 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;
}
筛法计算欧拉函数
可以通过线性筛来算某一个数的所有质数,从而提高求欧拉函数的效率。
ll get_eulers(int n){
phi[1] = 1;
for(int i = 2; i <= n; i++){
//如果i到当前还没有被筛掉的话,则证明i是一个质数
if(!st[i]){
primes[cnt ++] = i;
//1~i-1以内与质数i互质的个数有
phi[i] = i - 1;
}
//把所有i的倍数都筛掉
for(int j = 0; primes[j] <= n / i; j++){
st[primes[j] * i] = 1;
if(i % primes[j] == 0){
//公式推导,求i*prime[j]的质因子个数
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
ll res = 0;
for(int i = 1; i <= n; i++) res += phi[i];
return res;
}
欧拉定理
如果
费马小定理
如果
5. 快速幂
快速幂:快速求解
其核心思路是预处理出来
该式子即把k以二进制的形式表示。
int quick_mi(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;
}
快速幂求逆元
逆元:如果
这个数
左右两边把
由费马小定理可得到,如果
对比逆元的公式,如果b是质数,那么b的逆元即等于
int res = quick_mi(a, p-2, p);
//如果a和p互质,那么a的p-2次方即为逆元
if(a % p != 0) printf("%d\n", res);
//如果p是a的因子,那么对于a而言,不存在逆元
else print("impossible");
6. 扩展欧几里得算法
裴蜀定理
对于任意一对正整数
利用扩展欧几里得算法来求其系数
int exgcd(int a, int b, int &x, int &y){
//b==0时找到结果
if(!b){
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
//公式推导
y -= a / b * x;
return d;
}
int main(){
int a, b, x, y;
scanf("%d %d %d %d", &a, &b, &x, &y);
exgcd(a, b, x, y);
}
公式推导
边界条件:
当
又因为
所以
递归情况:
交换x、y,把
所以递归后的系数,
本文作者:hsy2093
本文链接:https://www.cnblogs.com/hsy2093/p/18277715
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步