hihocoder第九十三周 数论二·Eular质数筛法

本文介绍Eular质数筛法,一种O(n)时间复杂度的高效质数筛选算法。通过对比Eratosthenes筛法,展示Eular筛法如何避免重复标记合数,从而提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

#1295 : 数论二·Eular质数筛法

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB
描述

小Ho:小Hi,上次我学会了如何检测一个数是否是质数。于是我又有了一个新的问题,我如何去快速得求解[1,N]这个区间内素数的个数呢?

小Hi:你自己有什么想法么?

小Ho:有!我一开始的想法是,自然我们已经知道了如何快速判定一个数是否是质数,那么我就直接将[1,N]之间每一个数判定一次,就可以得到结果。但我发现这个方法太笨了。

小Hi:确实呢,虽然我们已经通过快速素数检测将每一次判定的时间复杂度降低,但是N个数字的话,总的时间复杂度依旧很高。

小Ho:是的,所以后来我改变了我的算法。我发现如果一个数p是质数的话,那么它的倍数一定都是质数。所以我建立了一个布尔类型的数组isPrime,初始化都为true。我从2开始枚举,当我找到一个isPrime[p]仍然为true时,可以确定p一定是一个质数。接着我再将N以内所有p的倍数全部设定为isPrime[p*i]=false。

写成伪代码为:

isPrime[] = true
primeCount = 0
For i = 2 .. N
	If isPrime[i] Then
		primeCount = primeCount + 1
		multiple = 2
		While (i * multiple ≤ N)
			isPrime[i * multiple] = false
			multiple = multiple + 1
		End While 
	End If
End For
  

小Hi:小Ho你用的这个算法叫做Eratosthenes筛法,是一种非常古老的质数筛选算法。其时间复杂度为O(n log log n)。但是这个算法有一个冗余的地方:比如合数10,在枚举2的时候我们判定了一次,在枚举5的时候我们又判定了一次。因此使得其时间复杂度比O(n)要高。

小Ho:那有没有什么办法可以避免啊?

小Hi:当然有了,一个改进的方法叫做Eular筛法,其时间复杂度是O(n)的。

提示:Eular质数筛法

输入

第1行:1个正整数n,表示数字的个数,2≤n≤1,000,000。

输出

第1行:1个整数,表示从1到n中质数的个数

样例输入
9
样例输出
4
这个质数筛选我觉得还是很神奇的,一直以为O(n log long n)的复杂度已经够低了,没想到还能做到O(n)的复杂度,数论真的很神奇。。。没啥好说的,hihocoder的提示里面也讲得很清楚,这两天就是根据hihocoder里面的92-97周的题目学到了数论里面比较玄学的操作。


#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<time.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 +5;
bool isPrime[maxn];
int prime[maxn];
int primecount = 0;
int solve(int n){
    for(int i = 2 ; i <= n ; i ++){
        if(isPrime[i]){
            primecount++;
            prime[primecount] = i;
        }
        for(int j = 1 ; j <= primecount ; j ++){
            if(i*prime[j] > n)
                break;
            isPrime[i*prime[j]] = false;
            if(i%prime[j] == 0)
                break;
        }
    }
    return primecount;
}
int main()
{
    int n;
    while(~scanf("%d" , &n)){
        memset(isPrime , true , sizeof(isPrime));
        primecount = 0;
        cout<<solve(n)<<endl;
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值