hihocoder 1295.线性筛

486


题目



点击显/隐题目

小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




题解



模板题


代码


点击显/隐代码
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1000005;

int prime[maxn], num_prime;
bool isNotPrime[maxn];

void PRIME(int n) {
    num_prime = 0;
    memset(prime, 0, sizeof(prime));
    memset(isNotPrime, false, sizeof(isNotPrime));
    isNotPrime[0] = isNotPrime[1] = true;

    for (long i = 2; i < n; i++) {
        if (!isNotPrime[i])
            prime[num_prime++] = i;
        for (int j = 0; j < num_prime && i * prime[j] < n; j++) {
            isNotPrime[i * prime[j]] = true;
            if (!(i % prime[j]))
                break;
        }
    }
}

int main() {
    int n;
    PRIME(maxn-2);

    while (scanf("%d", &n) != EOF)
        printf("%d\n", upper_bound(prime, prime + num_prime, n) - prime);
    return 0;
}
发布评论
  • 点击查看/关闭被识别为广告的评论