前言
之前笔试写到了一个素数判断,但是超时了(尴尬,当时知道用欧拉筛,但是忘记怎么写了),于是决定写一篇博客加深下印象 。
绝对不是水博客
普通筛素数
- 思路:先筛掉除了2之外2的倍数,然后i从3开始循环,每次+2,结束条件i*i<n,剩下活着的数就是素数啦
- 代码
//判断一个数是不是素数
//当然可以用数组来存状态
bool f(int n){
if(n == 2) return true;
if(n%2 == 0) return false;
for(int i = 3; i*i <= n; i+=2){
if(n%i == 0) return false;
}
return true;
}
那如果我们用数组的下标来存状态判断是不是素数呢?
我们将素数从2开始挨个乘积的下标标记为非素数
int t[10000]={0};
void f1(int n){
t[0] = t[1] = 1;
for(int i = 2; i*i < n && !t[i]; i++){
if(t[i]) continue;
for(int j = 2; j <= i; j++){
t[i*j] = 1;//标记为非素数
}
}
}
这样t[i]=0的 i 就是素数。但是这样很容易发现一个缺点,就是会重复晒出一些数。比如12会被26、34、43、62重复筛选,这个时候就轮到欧拉筛选出场了,时间复杂度O(n)。
欧拉筛选素数
- 参考例题:洛谷P3912
- 题目传送门:P3912素数个数
- 题目大概意思:
求 1,2,⋯,N 中素数的个数。
对于 100% 的数据,1<n<1e8
思路:
欧拉筛选法求素数,通过质数来筛掉他的倍数
关键:每个数都被他的最小质因数筛掉
这样就可以避免重复筛选
5800000//10^8的范围大概有5700000多个素数
要用bool类型的数组判断,不然会超内存
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
using namespace std;
/*
欧拉筛选法求素数
通过质数来筛掉他的倍数
关键:每个数都被他的最小质因数筛掉
5800000//10^8的范围大概有5700000多个素数
*/
const int MaxN = 100000000+5;
bool dp[MaxN];//判断下标是否为素数
//用int数组会超内存
int prim[5800000] = {0};//记录素数的值
ll cnt = 0;
bool f(int n){
if(n == 2) return true;
if(n%2 == 0) return false;
for(int i = 3; i*i <= n; i+=2){
if(n%i == 0) return false;
}
return true;
}
int main()
{
int n;
cin >> n;
dp[0] = dp[1] = 1;//0表示素数
for(int i = 2; i <= n; i++){
if(!dp[i]){
//i是素数
prim[cnt++] = i;
}
//用素数筛掉非素数
for(int j = 0; j<cnt&&i*prim[j]<=n; j++){
dp[i*prim[j]] = 1;
if(i%prim[j] == 0){
//这里很重要
//保证每一个数都是被它的最小质因数筛掉
//比如prim里面放2 3 5 7 11,此时i为12
// 2, 2; 2 3, 3*2 3*3;
//此时i=4,只筛掉2*4,不会筛掉4*3
//因为4*3=12要被6*2筛掉,保证被最小质因数筛掉
//这样避免重复筛
break;
}
}
}
cout << cnt << endl;
return 0;
}
总结
用最小质因数来筛选掉非素数,比如12只能被2 * 6筛,而不是被3 * 4筛掉。这样就可以避免重复筛除。