初等数论
开篇一句话——
数论是毒瘤
本博客内容基本按照李煜东神犇的《算法竞赛进阶指南》编排。
一、质数与约数
1. 定义:略。
2. 判定:
(1) 单个质数的判定——试除法
时间复杂度:
原理:不解释。
代码:
#include<math.h>
bool is_prime(int n){
if(n < 2)
return false;
for(int i = 2; i <= sqrt(n); i++)
if(n % i == 0)
return false;
return true;
}
(2) 质数的筛除
a. Eratosthenes筛法 (简称埃筛)
时间复杂度:
原理:任何整数
小优化:由于一个合数
代码:
#include<math.h>
#include<string.h>
#define N 10000005//可以修改
bool prime[N];
void is_prime(int n){
memset(prime, 0, sizeof(prime));
for(int i = 2; i <= n; i++) {
if(prime[i])//是合数了
continue;
for(int j = i; i * j <= n; j++)//j从i开始枚举是优化
prime[i * j] = true;//标记为合数
}
}
b.欧拉线性筛质数
时间复杂度:
原理:确定每个合数的枚举方式,令每个合数只有从大到小累计质因子这样唯一的分解方式。
具体的实现见代码注释:
#include<vector>
using namespace std;
#define N 10000005//可以修改
int v[N];
//v[i]表示i的最小质因数
vector<int>prime;
//prime数组标记每一个质数
void is_prime(int n){
for(int i = 2; i <= n; i++) {
if(v[i] == 0){//没有被筛掉,是合数
prime.push_back(i);
v[i] = i;//质数的最小质因子是自己
}
for(int j = 1; j <= (int)prime.size(); j++){//这一步是给当前的数i乘上一个质因子生成合数筛除
//令prime[j]为生成合数的最小质因子
if(prime[j] > v[i]/*不是最小的了*/ || prime[j] > n / i /*超过了sqrt(n),必然不是最小*/)
break;//强制停止枚举
v[i * prime[j]] = prime[j];//标记最小质因子
}
}
}
3.质因数分解
(1) 算术基本定理
任意一个
其中
(2)试除法分解质因数
原理:算术基本定理+埃筛
时间复杂度:
代码:
#include<math.h>
#define N 10000005//可以修改
int prime[N], cnt;
//prime数组标记每一个质数
int v[N];
//v[i]表示prime[i]的指数
void change(int n){
for(int i = 2; i <= sqrt(n); i++) {
if(n % i == 0){//i是质数
prime[++cnt] = i;
while(n % i != 0)//拆解干净
n /= i, v[cnt]++;
}
}
if(n > 1)//特判
prime[++cnt] = n, v[cnt] = 1;
}
(3)算术基本定理的推论
a. 约数个数
正整数
b.约数和
正整数
(4)求正约数集合——试除法
时间复杂度:
原理:约数总是成对出现的。
#include<math.h>
#define N 10000005//可以修改
int pri[N], cnt;
void change(int n) {
for(int i = 1; i <= sqrt(n); i++) {
if(n % i == 0) {//可以整除
pri[++cnt] = i;
if(i != n / i)
pri[++cnt] = n / i;//特判完全平方数的情况
}
}
}
(5)筛法求每个数的正整数集合
时间复杂度:
原理:对于每个数
代码:
#include<math.h>
#include<vector>
using namespace std;
#define N 10000005//可以修改
vector<int>prime[N];
void change(int n) {
for(int i = 1; i <= n; i++)
for(int j = 1; j * i <= n; j++)
prime[i * j].push_back(i);
}
4.最大公约数
1.定义:略。
2.辗转相除法求
代码:
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
5.欧拉函数
1. 定义
2.求单个数的欧拉函数
代码:
#include<math.h>
int pri(int n) {
int ans = n;
for(int i = 1; i <= sqrt(n); i++)
if(n % i == 0) {
ans = ans / i * (i - 1);//公式
while(n % i == 0)
n /= i;
}
if(n > 1)
ans = ans / n * (n - 1);
return ans;
}
二、同余
1.费马小定理
若
2.欧拉定理
(1)定理
若
(2)推论
若
若对于正整数
3.
(1) 定理——扩展欧几里得算法
对于任意整数
a.求解方程 的一组解。
运用Exgcd算法求解即可。
代码:
#include<iostream>
int exgcd(int a, int b, int &x, int &y) {
if(b == 0) {
x = 1;
y = 0;
//一组特解
return a;
}
int k = exgcd(b, a % b, x, y);
int tmp = x;
x = y;
y = tmp - y * (a / b);
return k;
}
int main() {
int a, b, x, y;
std::cin >> a >> b;
int g = exgcd(a, b, x, y);
//求x的最小正整数解
x = (x + b) % b;
y = (g - (a * x)) / b;
std::cout << x << ' ' << y << '\n';
return 0;
}
b.进一步地考虑,现在要求这个方程使x最小的整数解。
方程(x % b + b) % b
即可。
c.考虑更一般的情况。寻找方程 的求法。
记
由定理:该方程有整数解仅当
则该方程的一个解为
那么方程最小整数解则是
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】