筛素数

筛素数

1.暴力筛

#include <cstdio>
#include <iostream>
using namespace std;

int n, q;

bool check (int);

int main () {
    scanf ("%d %d", &n, &q);
    for (int i = 1; i <= q; i++) {
        int x; scanf ("%d", &x);
        if (check (x)) {
            printf ("Yes\n");
        }
        else {
            printf ("No\n");
        }
    }
    return 0;
}

bool check (int x) {
    if (x == 1) return 0;
    for (int i = 2; i <= x / i; i++) {
        if (x % i == 0) return 0;
    }
    return 1;
}

2.埃筛

#include <cstdio>
#include <iostream>
using namespace std;

const int MAXN = 100000005;

int n, q, len;
int prime[MAXN];

bool vis[MAXN];

void Make ();

int main () {
    scanf ("%d %d", &n, &q);
    Make ();
    for (int i = 1; i <= q; i++) {
        int x; scanf ("%d", &x);
        if (vis[x] == 0) {
            printf ("Yes\n");
        }
        else {
            printf ("No\n");
        }
    }
    return 0;
}

void Make () {
    vis[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (vis[i] == 0) {
            prime[++len] = i;
            for (int j = i; j <= n / i; j++) {
                vis[i * j] = 1;
            }
        }
    }
}

3.线性筛(欧拉筛)

#include <cstdio>
#include <iostream>
using namespace std;

const int MAXN = 100000005;
int n, m, num;
int prime[MAXN];

bool vis[MAXN];

void oulasai();

int main() {
    cin >> n >> m;
    oulasai();
    for (int i = 1; i <= m; i++) {
        int tem;
        scanf("%d", &tem);
        if (vis[tem] == 0)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

void oulasai() {
    vis[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (vis[i] == 0) {  //如果是质数
            prime[++num] = i;
        }
        for (int j = 1; j <= num; j++) {
            if (prime[j] > n / i)
                break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0)
                break;
        }
    }
}
<think>首先,用户的问题是:“质数的朴素算法实现或原理 请问质数 朴素算法 实现原理 时间复杂度”。我需要基于系统级指令和用户可见层的规则来回答。 系统级指令: - 所有行内数学表达式用 `$...$` 格式。 - 独立公式用 `$$...$$` 格式并单独成段。 - LaTeX 语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,如 `[^1]`。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 - 参考站内引用:引用[1]到[5]提供了关于质数的信息。 从引用中提取关键信息: - 引用[1]:描述了朴素法(时间复杂度 O(n log n)),代码示例使用 C++ 实现。 - 引用[2]:比较了朴素法、埃氏法和欧拉法,指出朴素法重复率高。 - 引用[3]:解释了线性法(欧拉法)如何改进埃氏法,时间复杂度 O(n)。 - 引用[4]:总结了三种法,包括朴素法,并提到时间复杂度。 - 引用[5]:讨论了算法时间复杂度,但主要针对其他问题;相关部分是多项式时间复杂度。 用户特别询问朴素法: - 实现原理:朴素法是通过遍历每个数,并标记其倍数作为合数。引用[1]中代码显示:从2开始,对于每个数i,如果没有被标记(即质数),则将其倍数全部标记为合数。 - 时间复杂度:引用[1]说 O(n log n),引用[4]也提到类似。计算:对于每个质数i,标记倍数的操作次数约为 n/i,所以总操作是 sum_{i=2}^n (n/i) ≈ n * H_n ≈ n log n,其中 H_n 是调和级数。 - 实现:引用[1]提供了 C++ 代码,我需要用中文解释它。 回答结构: 1. **介绍朴素法**:简要解释什么是质数的朴素算法。 2. **实现原理**:详细描述算法步骤。 3. **时间复杂度分析**:解释为什么是 O(n log n)。 4. **代码示例**:提供代码(基于引用[1]),并用中文注释。 5. **生成相关问题**:在结尾添加相关问题。 数学表达式: - 行内数学:如时间复杂度 $O(n \log n)$。 - 独立公式:如果必要,但这里可能不需要复杂的公式。 引用标识: - 当引用信息时,在末尾添加 `[^1]` 等。例如,引用引用[1]的代码或描述。 生成相关问题: - 基于主题,如其他法、优化、应用等。 完整回答大纲: 1. **开头**:直接回答问题。 2. **原理部分**:解释朴素法的工作原理。 3. **时间复杂度**:分析并给出数学依据。 4. **代码实现**:展示代码,解释关键部分。 5. **总结**:简要总结。 6. **相关问题**:列出3-5个相关问题。 确保中文回答。 最终输出: - 使用 Markdown 格式。 - 代码块用 ``` 包裹。 - 相关问题用 "§§相关问题§§" 开头。</think>### 质数的朴素算法:实现原理与时间复杂度分析 质数(又称素数选)是一种用于高效找出给定范围内所有质数的算法。朴素法(Naive Sieve)是最基础的一种实现,其核心思想是通过遍历和标记合数来选质数。下面我将逐步解释其实现原理、时间复杂度,并提供代码示例。内容基于可靠算法知识,并结合了站内引用信息[^1][^2][^4]。 #### 1. **实现原理** 朴素法的原理基于一个简单事实:所有合数都可以被分解为更小的质因数。算法步骤如下: - **初始化**:创建一个布尔数组 `st`(大小至少为 $n+1$),初始假设所有数(从2开始)都是质数(即 `st[i] = false` 表示 $i$ 是质数)。 - **遍历数字**:从 $i = 2$ 开始遍历到 $n$。 - 如果 `st[i]` 为 `false`(即 $i$ 是质数),则: - 将 $i$ 加入质数列表。 - 标记 $i$ 的所有倍数(如 $i \times j$,其中 $j \geq i$)为合数(设置 `st[i * j] = true`)。 - **结束条件**:遍历完成后,所有未被标记的数即为质数。 关键点: - 算法重复标记每个数的倍数,包括质数和合数本身(如标记 $4$ 时,$2$ 的倍数已被标记,但 $4$ 仍会被再次处理)。 - 这导致高重复率,因为一个合数可能被多个质因数多次标记(例如,$6$ 会被 $2$ 和 $3$ 都标记)[^2][^4]。 - 时间复杂度较高,但实现简单易懂。 #### 2. **时间复杂度分析** 朴素法的时间复杂度为 $O(n \log n)$。原因如下: - **操作计数**:对于每个数 $i$($2 \leq i \leq n$),算法需要标记其倍数。倍数标记的次数约为 $\frac{n}{i}$(因为 $j$ 从 $i$ 开始,步长为 $i$,直到 $j \leq n$)。 - **总操作量**:所有 $i$ 的标记操作总和为 $\sum_{i=2}^{n} \frac{n}{i}$。 - **调和级数近似**:该和式可近似为 $n \times H_n$,其中 $H_n$ 是调和级数($H_n = \sum_{k=1}^{n} \frac{1}{k} \approx \log n + \gamma$,$\gamma$ 为欧拉常数)。因此,总操作量约为 $n \log n$。 - **最终复杂度**:忽略常数项,时间复杂度为 $O(n \log n)$。 相比于更高效的法(如埃氏法 $O(n \log \log n)$ 或线性法 $O(n)$),朴素法在 $n$ 较大时性能较差,因为其重复标记导致额外开销[^2][^3][^4]。 #### 3. **代码实现(C++ 示例)** 以下代码基于引用[^1]的实现,我用中文添加了详细注释。代码找出小于等于 $n$ 的所有质数,并输出质数个数。 ```cpp #include <iostream> #include <vector> using namespace std; // 朴素法实现 void naive_sieve(int n) { vector<bool> st(n+1, false); // st[i] 为 true 表示 i 是合数,false 表示质数(初始假设所有数都是质数) vector<int> primes; // 存储质数列表 int cnt = 0; // 质数计数器 // 从2开始遍历到n for (int i = 2; i <= n; i++) { if (st[i]) continue; // 如果 i 已被标记为合数,跳过 primes.push_back(i); // i 是质数,加入列表 cnt++; // 质数计数增加 // 标记 i 的所有倍数(从 i*i 开始优化,但朴素版本通常从 i 开始) for (int j = i; j <= n; j += i) { st[j] = true; // 标记 j 为合数 } } // 输出结果 cout << "小于等于 " << n << " 的质数个数: " << cnt << endl; cout << "质数列表: "; for (int prime : primes) { cout << prime << " "; } cout << endl; } int main() { int n; cout << "请输入 n: "; cin >> n; naive_sieve(n); return 0; } ``` **代码说明**: - **输入**:用户输入 $n$(例如 $n=100$)。 - **输出**:质数个数和列表(如小于等于 100 的质数有 25 个)。 - **优化点**:内层循环从 $j = i$ 开始(而非 $j = 2$),但标记所有倍数(包括 $i$ 本身),这保证了所有合数被覆盖,但增加了冗余。 - **缺点**:如引用[^2][^4]指出,合数会被多次标记(例如 $6$ 被 $2$ 和 $3$ 都标记),导致效率低下。 #### 4. **总结** 朴素法通过遍历和标记倍数来选质数,实现简单但时间复杂度为 $O(n \log n)$。当 $n$ 较小时(如 $n \leq 10^5$),它仍可用;但当 $n$ 较大时,推荐使用埃氏法($O(n \log \log n)$)或线性法($O(n)$)以提高性能[^2][^3][^4]。如果您需要其他法的细节或优化版本,请随时提问!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值