问题描述
给定一个正整数 n nn,请你求出 1 ∼ n 1∼n1∼n 中质数的个数。
输入格式
共一行,包含整数 n。
输出格式
共一行,包含一个整数,表示 1∼n 中质数的个数。
数据范围
1 ≤ n ≤ 1 0 6 1≤n≤10^61≤n≤106
解决方法
朴素筛法
从前往后遍历,把每个数的倍数都删掉,剩下的数就是质数
证明方法在前面的一个打卡里面写了,复杂度是O(nlogn)
这里优化一下,只需要把所有质数的倍数删掉即可,证明也是在上一篇文章里面讲了
这里是时间复杂度为O(nloglogn),这里把它叫做埃氏筛法
代码实现:
#include<iostream> #include<algorithm> using namespace std; const int N = 1000010; bool st[N]; int cnt, primes[N]; int 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) st[j] = true; } } return cnt; } int main(){ int n; cin >> n; int res = get_primes(n); cout << res << endl; return 0; }
线性筛法
思想:把每一个合数用它的某一个质因子筛掉即可
每个合数x一定会被筛掉,而且筛的时候一定用的是最小质因子,
而且每个数只有一个最小质因子,所以每个数只会被筛一次,所以是线性的。
代码实现:
#include<iostream> #include<algorithm> using namespace std; const int N = 1000010; bool st[N]; int cnt, primes[N]; int 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; //把primes[j]这个质数的某个倍数筛掉即可(核心思想) //这个合数是前面某一个质数的倍数,已经筛掉了 if(i % primes[j] == 0) break; //(因为是从小到达枚举的所有的质数,所以第一次出现的primes[j]一定是i的最小质因子) } } return cnt; } int main(){ int n; cin >> n; int res = get_primes(n); cout << res << endl; return 0; }
作者:为梦而生
链接:https://www.acwing.com/file_system/file/content/whole/index/content/10509230/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。