C++埃氏筛

1 篇文章 0 订阅

引入

题目背景

给定正整数 n n n,请求出 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n间所有质数

输入格式

一个正整数 n n n

输出格式

一行多个正整数,表示 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n中所有质数。

样例

样例 #1

样例输入 #1
3
样例输出 #1
2 3

数据范围

  • 对于 100 % 100 \% 100% 的数据, 1 ≤ n ≤ 1 0 6 1\leq n\leq10^6 1n106

暴力解决

遇事不决先暴力——洛谷大犇

最基础的思想就是枚举一遍 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n,遇到质数就输出。
实现代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 1;
int n;
bool isprime(int x)
{
    if (x == 1) return 0;
    int i = 2;
    while (i < x) {
        if (x % i == 0) return 0;
        ++i;
    }
    return 1;
}
int main()
{
    scanf("%d", &n);
    for (int i = 2; i <= n; ++i)
        if (isprime(i)) printf("%d ", i);
       printf("\n");
    return 0;
}

时间复杂度: O ( n 2 ) O(n^2) O(n2)

我们注意到,任何数 i ( x ≤ i < x ) i(\sqrt x\leq i<x) i(x i<x) 一定不整除 x x x
所以枚举 i i i 时只需枚举 2 , ⋯   , x 2,\cdots,\sqrt x 2,,x 即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 1;
int n;
bool isprime(int x)
{
    if (x == 1) return 0;
    int i = 2;
    while (i * i <= x) { // any number that is bigger than sqrt(x) and smaller than x won't be a factor of x
        if (x % i == 0) return 0;
        ++i;
    }
    return 1;
}
int main()
{
    scanf("%d", &n);
    for (int i = 2; i <= n; ++i)
        if (isprime(i)) printf("%d ", i);
       printf("\n");
    return 0;
}

于是时间复杂度降到 O ( n n ) O(n\sqrt n) O(nn )

埃氏筛基本思想

上面的代码中,每一个数都要判断是否被 2 2 2 整除。但是我们明显可以看到,只有 2 2 2是质数,其他偶数必定不是质数。所以这样实质上浪费了 O ( n ) O(n) O(n)的时间。
同理,对于一个质数 p p p k p ( k ≥ 2 且 k ∈ Z + ) kp(k\geq2且k\in\Z^+) kp(k2kZ+)1 必有 p p p 这个因数,所以 k p kp kp 一定不是质数。
那么,我们可以想到,不枚举 1 , ⋯   , n 1,\cdots,n 1,,n,转而枚举 p 1 , p 2 , ⋯   , p m p_1,p_2,\cdots,p_m p1,p2,,pm,并将 { x ∣ x = k p i } ( k ≥ 2 ) \{x\mid x=kp_i\}(k\geq2) {xx=kpi}(k2) 这个集合里所有数打上非质数标记(这里用布尔值最为方便,1表示不是质数)。

埃氏筛模版

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 1;
int n;
bool p[N] = {1, 1}; // p[i]: if i is not prime then p[i] = 1, otherwise it'll be 0
void fillp()
{
	for (int i = 2; i * i <= n; ++i)
		if (!p[i])
			for (int j = 2; i * j <= n; ++j) p[i * j] = 1;
}
int main()
{
    scanf("%d", &n);
    fillp();
    for (int i = 2; i <= n; ++i)
    	if (!p[i]) printf("%d ", i);
    return 0;
}

这样,我们省下很多重复的计算。
时间复杂度: O ( n log ⁡ log ⁡ n ) O(n\log\log n) O(nloglogn)2
这并不是没有代价的,空间复杂度从 O ( 1 ) O(1) O(1) 升到了 O ( n ) O(n) O(n),类似dp以空间换时间的做法。


  1. Z + \Z^+ Z+为正整数集,即 { 1 , 2 , ⋯   } \{1,2,\cdots\} {1,2,} ↩︎

  2. 作者觉得还可以的证明:有点不严谨,但不繁琐 ↩︎

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值