线性筛素数计数

线性筛素数计数

http://ybt.ssoier.cn:8088/problem_show.php?pid=1151

编程求2∼n中有多少个素数。
用线性筛

💡 思路(线性筛 / 欧拉筛)

线性筛的核心思想:

  1. 每个合数都只会被它的 最小质因子(LPF)筛掉一次;
  2. 保证全部筛操作的次数为 \(O(n)\)

关键规则:

  • 枚举 i 从 2 到 n
  • i 未被标记,是素数 → 加入素数表
  • 对所有已有素数 p
    • 标记 i * p 为合数
    • 如果 pi 的最小质因子(i % p == 0),就停止继续乘更大的质数(确保线性复杂度)

✅ C++ 代码(OI 风格 + 详细注释)

#include <bits/stdc++.h>
using namespace std;

// 线性筛模板:统计 2~n 的素数个数
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    vector<int> primes;           // 存放所有素数
    vector<bool> is_composite(n+1, false); // 标记合数

    // 欧拉线性筛
    for (int i = 2; i <= n; i++) {

        // 若 i 未被标记,则为素数
        if (!is_composite[i]) {
            primes.push_back(i);
        }

        // 用当前所有已知素数筛掉 i*p
        for (int p : primes) {

            // 超界则退出
            if (1LL * i * p > n) break;

            is_composite[i * p] = true;     // 标记为合数

            // 若 p 是 i 的最小质因子,则停止继续
            // 保证每个合数只被处理一次(线性复杂度核心)
            if (i % p == 0) break;
        }
    }

    // 输出素数个数
    cout << primes.size() << "\n";
    return 0;
}

📌 复杂度分析

  • 时间复杂度: \(O(n)\)
    每个整数只会被筛一次。
  • 空间复杂度: \(O(n)\)

如果你需要版本:输出所有素数 / 多次询问 / 给出素数表上界优化,也可以继续问我!

Prompt:

下面这个代码错在哪里?

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+100;
bool visit[N];
int prime[N],n,x,cnt1;
void init(int n){
	int cnt=0;
	for(int i=2;i<=n;i++){
		if(!visit[i]) prime[cnt++]=i;
		for(int j=0;j<cnt;j++){
			if(i*prime[j]>n) break;
			visit[i*prime[j]]=1;
            cnt1++;
			if(i%prime[j]==0) break;
		}
	}
}
int main(){
	cin>>n;
	init(n);
	cout<<cnt1-1;
    return 0;
}

Response:

下面指出你代码中的 核心错误与原因,并给出 正确写法


❌ 你的代码中存在的主要问题

① 你输出的是 cnt1-1,但 cnt1 根本不是素数个数

你把 cnt1 写成:

cnt1++;

它是在 每次筛掉一个数时 +1
也就是「筛选操作次数」,不是「素数数量」!

所以:

  • cnt1 ≠ 素数数量
  • cnt1 - 1 更是完全没有意义

因此逻辑错误,输出错。


② prime[] 里记录素数数量的变量 cnt 是局部变量

你写了:

int prime[N], n, x, cnt1;

void init(int n){
    int cnt = 0;   // <-- 局部变量!

问题:

  • cntinit() 内是局部的
  • main() 里无法获取到素数数量
  • prime[] 也没有全局保存素数个数信息

所以程序最终无法得知 prime 里存了多少素数。


③ 输出素数个数应该输出的是 cnt

而不是筛掉多少个数。


✅ 正确写法(修正版)

#include <bits/stdc++.h>
using namespace std;

const int N = 5e5 + 100;
bool vis[N];
int prime[N], n, cnt = 0;   // cnt 全局,表示素数个数

void init(int n) {
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) prime[cnt++] = i;        // 记录素数

        for (int j = 0; j < cnt; j++) {
            if (1LL * i * prime[j] > n) break;

            vis[i * prime[j]] = true;

            if (i % prime[j] == 0) break;     // 关键剪枝
        }
    }
}

int main() {
    cin >> n;
    init(n);
    cout << cnt << "\n";      // 输出素数个数
    return 0;
}

📌 总结

错误 原因
cnt1 统计的是筛掉次数 不是素数个数,输出错
素数计数 cnt 写在函数内部 main 拿不到,逻辑错误
最终输出 cnt1-1 完全不正确

正确做法:用全局 cnt 来统计素数数量并输出。


posted @ 2025-12-12 16:08  katago  阅读(0)  评论(0)    收藏  举报