【算法】数论——筛质数(线性筛法)

题目

给定一个正整数 n,请你求出 1∼n 中质数的个数。

输入格式

共一行,包含整数 n。

输出格式

共一行,包含一个整数,表示 1∼n 中质数的个数。

数据范围

1 ≤ n ≤ 10^6

思路

朴素筛法
做法 :把2~(n-1)中的所有的数的倍数都标记上,最后没有被标记的数就是质数.
原理 :假定有一个数p未被2~(p-1)中的数标记过,那么说明,不存在2~(p-1)中的任何一个数的倍数是p,也就是说p不是2~(p-1)中的任何数的倍数,也就是说2~(p-1)中不存在p的约数,因此,根据质数的定义可知:p是质数.
时间复杂度:约为O(nlogn);

埃氏筛(稍加优化版的筛法)
质数定理 :1~n中有n/lnn个质数.
原理 :在朴素筛法的过程中只用质数项去筛.
时间复杂度:粗略估计:O(n).实际:O(nlog(logn)).1~n中,只计算质数项的话,"1/2+1/3+1/4+1/5+...+1/n"的大小约为log(logn)。

线性筛法 核心 :1~n内的合数p只会被其最小质因子筛掉.
原理:1~n之内的任何一个合数一定会被筛掉,而且筛的时候只用最小质因子来筛,然后每一个数都只有一个最小质因子,因此每个数都只会被筛一次,因此线性筛法是线性的.

注意:

1、枚举到 i 的最小质因子的时候就会停下来,即" if(i%primes[j]==0) break ; "。

2、因为从小到大枚举的所有质数,所以当" i % primes[j] != 0 "时,primes[ j ]一定小于 i 的最小质因子,而且 primes[j] 一定是primes[j] * i的最小质因子。

3、关于for循环的解释:

注:首先要把握住一个重点:我们枚举的时候是从小到大枚举的所有质数

(1)当i%primes[j]==0时,因为是从小到大枚举的所有质数,所以primes[j]就是i的最小质因子,而primes[j]又是其本身primes[j]的最小质因子,因此当i%primes[j]==0时,primes[j]是primes[j] * i的最小质因子。

(2)当 i%primes[j]!=0 时,因为是从小到大枚举的所有质数,且此时并没有出现过有质数满足i%primes[j] == 0,因此此时的primes[j]一定小于i的最小质因子,而primes[j]又是其本身primes[j]的最小质因子,所以当i%primes[j]!=0时,primes[j]也是primes[j] * i的最小质因子。

(3)综合1,2得知,在内层for循环里面无论何时,primes[j]都是primes[j] * i的最小质因子,因此 "st[primes[j * i] = true"语句就是用primes[j]i这个数的最小质因子来筛掉这个数。

代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int primes[N],cnt;
bool st[N];// 储存该值i是不是为非质数

void get_primes(int n)
{
    for(int i = 2;i <= n; i ++)// 依次判断2~n是否为质数
    {
        if(!st[i]) primes[cnt ++] = i;// 如果该数是质数则将其储存起来,如果不是质数则不储存
        for(int j = 0;primes[j] <= n / i; j ++)// 筛掉是i的primes[j]倍的所有合数
        {
            st[primes[j] * i] = true;// 确保每个合数是被他最小的质因子筛掉的
            if(i % primes[j] == 0) break;// primes[j]一定是i的最小质因子
            //p[j]一定是i的最小质因子,pj也一定是pj*i的最小质因子
        }
    }
}

int main()
{
    int n;
    cin >> n;
    get_primes(n);
    cout << cnt << endl;
    return 0;
}
相关推荐
Mr_Xuhhh16 分钟前
信号与槽的总结
java·开发语言·数据库·c++·qt·系统架构
github_czy31 分钟前
RRF (Reciprocal Rank Fusion) 排序算法详解
算法·排序算法
liulilittle31 分钟前
VGW 虚拟网关用户手册 (PPP PRIVATE NETWORK 基础设施)
开发语言·网络·c++·网关·智能路由器·路由器·通信
许愿与你永世安宁1 小时前
力扣343 整数拆分
数据结构·算法·leetcode
爱coding的橙子1 小时前
每日算法刷题Day42 7.5:leetcode前缀和3道题,用时2h
算法·leetcode·职场和发展
ruanjiananquan991 小时前
c,c++语言的栈内存、堆内存及任意读写内存
java·c语言·c++
满分观察网友z2 小时前
从一次手滑,我洞悉了用户输入的所有可能性(3330. 找到初始输入字符串 I)
算法
持梦远方2 小时前
C 语言基础入门:基本数据类型与运算符详解
c语言·开发语言·c++
YuTaoShao2 小时前
【LeetCode 热题 100】73. 矩阵置零——(解法二)空间复杂度 O(1)
java·算法·leetcode·矩阵
Heartoxx2 小时前
c语言-指针(数组)练习2
c语言·数据结构·算法