初等数论

开篇一句话——

数论是毒瘤

本博客内容基本按照李煜东神犇的《算法竞赛进阶指南》编排。

一、质数与约数

1. 定义:略。

2. 判定:

(1) 单个质数的判定——试除法

时间复杂度: O(n)

原理:不解释。

代码:

#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筛法 (简称埃筛)

时间复杂度: O(n log log n)

原理:任何整数 x 的正整倍数,均不是质数。则我们可以扫描尚未被标记的数,即质数,把它的倍数标记,直到超过了 n

小优化:由于一个合数 x 一定含有 x 的正因子,因此扫描到一个数 x 必定在 x被筛掉了,反之我们可以考虑对于每个质数,我们从x2开始筛除即可。

代码:

#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.欧拉线性筛质数

时间复杂度: O(n)

原理:确定每个合数的枚举方式,令每个合数只有从大到小累计质因子这样唯一的分解方式。

具体的实现见代码注释:

#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) 算术基本定理

任意一个 >1 的自然数可以被唯一分解为有限个质数的乘积,可以写为:

N=p1c1p2c2...pmcm

其中ci均为正整数,pi均为质数,且满足p1<p2<...<pm

(2)试除法分解质因数

原理:算术基本定理+埃筛

时间复杂度:O(n)

代码:

#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. 约数个数

正整数N的正约数个数为 (c1+1)  (c2+1)   (cm+1)

b.约数和

正整数N的正约数的和为(1+p1+p12++p1cm)  (1+pm+pm2++pmcm)

(4)求正约数集合——试除法

时间复杂度: O(n)

原理:约数总是成对出现的。

#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)筛法求每个数的正整数集合

时间复杂度:O(nlogn)

原理:对于每个数t,以t为约数的数即为d的倍数,则在枚举每个数时再枚举它的倍数,在它的倍数集合中插入这个元素即可。

代码:

#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.辗转相除法求gcd

b0, gcd(a, b)=gcd(b, a mod b)

代码:

int gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}

5.欧拉函数

1. 定义

1 ~ N中与N互质的数的个数,记为φ(n)

2.求单个数的欧拉函数

φ(n)=Np11p1p21p2pm1pm

代码:

#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.费马小定理

p是质数,则对于任意整数a,有apa (mod p)

2.欧拉定理

(1)定理

gcd(a,n)=1,则aφ(n) 1 (mod n)

(2)推论

gcd(a,n)=1,则对于任意正整数b,有abab mod φ(n)(mod n)

若对于正整数bb>φ(n),则有abab mod φ(n)+φ(n)(mod n)

3.Exgcd

(1)Bezout定理——扩展欧几里得算法

对于任意整数a, b,存在一对整数x, y,满足ax+by=gcd(a,b)

a.求解方程ax+by=gcd(a,b)的一组解。

运用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最小的整数解。

方程ax+by=c,在有了x的一个解,记为x0,我们又知道b的值,那么通解即为mod bx0同余的整数。则只需要求解(x % b + b) % b即可。

c.考虑更一般的情况。寻找方程ax+by=c的求法。

g=gcd(a,b), x0为方程ax+by=gcd(a,b)的一个解。

由定理:该方程有整数解仅当g|c

则该方程的一个解为x=x0b/g

那么方程最小整数解则是 x=(x0%(b/g)+b/g)%(b/g)

posted @   长安19路  阅读(11)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示