火龙信奥
  • 首页
  • 课程
  • 题库
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

第二课 数论基础

作者: 作者的头像   huolong , 时间:2023-01-09 12:56:14 , 所有人可见, 阅读  13

  1. 质数的判断 质数:在大于1的整数中,如果只包含1和它本身这两个约数,就被成为质数,或者叫素数.

如果 d | n, 则(n / d) | n. ------ 可以发现n的所有约数都是成双成对出现的.

质数定理: 1 - n中有n / ln(n) 个质数.

调和级数: 1 + 1 / 2 + 1 / 3 + 1 / 4 + ··· = lnn + C(0.577)

1.朴素做法 ---- $O(n)$

bool is_prime(int n){
    if(n < 2) return false;
    for(int i = 2; i < n; i ++){
        if(n % i == 0){
            return false;
        }
    }
    return true;
}

2.改进版 ---- $O(\sqrt{n})$(一定是)

bool is_prime(int n){
    if(n < 2) return false;
    for(int i = 2; i <= n / i; i ++){//约数成对出现,i 和 n / i, 枚举到较小的那个即可. 
        if(n % i == 0){
            return false;
        }
    }
    return true;
}

尽量不要写成: 1. for(int i = 2; i * i <= n; i ++) // 但 i 较大时, i * i 可能超出int的最大表示范围,导致结果出错. 2. for(int i = 2; i <= sqrt(n); i ++) // 反复调用sqrt()函数, 运行效率比较低

建议写成: for(int i = 2; i <= n / i; i ++) // 可以很好地规避上述情况

典型例题 试除法判定质数

  1. 分解质因数 分析:假设枚举到 i, n 中一定不包含 2 ~ i - 1 之间的质因子, 又因为n % i == 0,即 n 为 i 的倍数, 所以 i 当中也不包含 2 ~ i - 1 之间的质因子, 所以 i 一定是质数. 1.朴素做法 ---- $O(n)$
void divide(int n){
    for(int i = 2; i <= n;  i ++){
        if(n % i == 0){
            int s = 0;
            while(n % i == 0){
                n /= i;
                s ++;
            }

            cout << i << ' ' << s << endl;
        }
    }
}

2.改进版 ---- $O(\sqrt{n})$(不一定)

void divide(int n){
    for(int i = 2; i <= n / i; i ++){//n中最多包含一个大于sqrt(n)的质因子
        if(n % i ==0){
            int s = 0;
            while(n % i == 0){
                n /= i;
                s ++;
            }
            cout << i << ' ' << s << endl;
        }
    }
    if(n > 1) cout << n << ' ' << '1' << endl;//大于sqrt(n)的质因子
}

注意:当 $n = 2^k$ 时, 时间复杂度为 $logn$, 因此其时间复杂度介于 $logn ~ \sqrt{n}$ 之间

典型例题 分解质因数

  1. 筛质数 1.朴素做法 ----$O(n∗logn)$

原理: 2 - p - 1中的数都没有把 p 删掉,说明 p 不是 2 - p - 1 当中任何一个数的倍数,因此 p 是一个质数.

int primes[N], cnt;     // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

void get_primes(int n){
    for(int i = 2; i <= n; i ++){
        if(!st[i]) primes[cnt ++] = i;
        for(int j = i + i; j <= n; j += i){//依次删掉每个i的倍数
            st[j] = true;
        }
    }
}

2.改进版 ---- $O(n∗loglogn)$ (埃氏筛法)

质数定理:1 - n 当中, 有 n / ln(n) 个质数.

int primes[N], cnt;     // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

void get_primes(int n){
    for(int i = 2; i <= n; i ++){
        if(!st[i]){
            primes[cnt ++] = i;
            for(int j = i + i; j <= n; j += i){//只删掉质数i的倍数
                st[j] = true;
            }
        }
    }
}

2.终极版 ---- $O(n)$ (线性筛法)

核心:每个 n 只会被它的最小质因子筛掉.

int primes[N], cnt;     // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

void get_primes(int n){
    for(int i = 2; i <= n; i ++){
        if(!st[i]) primes[cnt ++] = i;
        for(int j = 0; primes[j] <= n / i; j ++){
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) break;//primes[j]一定是i的最小质因子
        }
    }
}

分析: 1.i % primes[j] == 0, 说明:primes[j]一定是i的最小质因子,primes[j]一定是primes[j] * i的最小质因子 2.i % primes[j] != 0, 说明: primes[j]一定小于i的所有质因子,primes[j]一定是primes[j] * i的最小质因子 综上:primes[j]一定是primes[j] * i的最小质因子.

我们用最小质因子筛,而每个数只有一个最小质因子,因此每个数只会被筛一次,所以是线性的.

注意:如果在1e6左右,埃氏筛法和线性筛法差不多,但在1e7左右,线性筛法要比埃氏筛法快几倍.在实际应用中线性筛法用的比较多,埃氏筛法用的较少,但埃氏筛法的思想很重要.

—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

©2026加盟我们 | 关于我们 | ACM课程 | 常见问题 | 成果墙 | 评测记录 | 浙ICP备2021013995号
在线画图 | OI WIki | 打字练习
火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码