众所周知,素数筛法许多种,今天我来比较时间。都是1e7以内的素数。
话不多说,开始比较(有错请指出):
1.暴力法:
一个一个枚举
#include<bits/stdc++.h>
using namespace std;
bool isPrime(long long num) {
for(long long i = 2; i <= num; i++) {
if(num % i == 0) {
return false;
}
}
if(num < 2) {
return false;
}
return true;
}
int main() {
int cnt = 0;
for(int i = 1; i <= 1e7; i++) {
if(isPrime(i) == 1) {
cnt++;
}
}
cout << cnt << endl;
return 0;
}
这个太慢了,大约是1600万秒(≈185天),效率最低。
2.优化版:
由一个一个枚举变成了从一开始到sqrt(i)。
#include<bits/stdc++.h>
using namespace std;
bool isPrime(long long num) {
for(long long i = 2; i * i <= num; i++) {
if(num % i == 0) {
return false;
}
}
if(num < 2) {
return false;
}
return true;
}
int main() {
int cnt = 0;
for(int i = 1; i <= 1e7; i++) {
if(isPrime(i) == 1) {
cnt++;
}
}
cout << cnt << endl;
return 0;
}
这个5秒多,还是太低。
3.埃拉托斯特尼筛法(埃氏筛法)时间复杂度:O(nlogn)
思想:
- 初始化一个布尔数组,表示每个数是否为素数,初始值为True。
- 从2开始遍历到n,对于每个素数p,将其倍数p×k(其中k>1)标记为非素数。
- 遍历完整个范围后,未被标记的数即为素数
我这个不是按上面来的
#include<bits/stdc++.h>
using namespace std;
bool isPrime(long long num) {
for(long long i = 2; i * i <= num; i++) {
if(num % i == 0) {
return false;
}
}
if(num < 2) {
return false;
}
return true;
}
int main() {
int cnt = 0;
for(int i = 1; i <= 1e7; i++) {
if(isPrime(i) == 1) {
cnt++;
}
}
cout << cnt << endl;
return 0;
}
0.5秒多,还可以。
4.欧拉筛法(时间复杂度:O(n)):
思路:
从2开始,依次遍历每一个数,循环变量i通过a[n]可以判断这个数i是不是素数如果a[]是合数
用a[i]依次乘以已经找到的所有素数,得到一些新的合数
如果i是这个素数的倍数停止标记。
比如数字4是合数
当前找到的素数有2,3
对第一个素数2,4*2=8,将8标记为合数,判断4是不是2的倍数,是,结束标记
对第二个素数3,已经结束标记不再进行操作。
#include<bits/stdc++.h>
using namespace std;
int index = 0;
const int n = 1e7;
int a[n+1] = {0};
int b[n+1] = {0};
int main()
{
for(int i=2;i<=n;i++){
if(a[i]==0){
b[index++] = i;
for(int j=0;(j<index)&&(i*b[j]<=n);j++){
a[i*b[j]] = 1;
}
}else{
for(int j=0;(j<index)&&(i*b[j]<=n);j++){
a[i*b[j]] = 1;
if(i%b[j] == 0){
break;
}
}
}
}
cout<<index;
return 0;
}
0.2秒多,厉害!
注:电脑硬件不同,速度不同,我的速度仅供参考。
感谢您的观看!