筛素数

本文介绍三种不同的素数筛选方法:枚举筛、埃式筛法和线性筛法,并提供了详细的C++实现代码。通过这些算法,可以在指定范围内高效地找出所有素数。

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

素数是一个非常神奇的东西,它的因子只有它自己和1,那么问题来了,怎么在一个有限的区间内打印出所有的素数呢?咱们就先来解决这一个基本的问题。

 

我们可以针对“它的因子只有它自己和1”这个特殊点来进行筛查。先判断区间内每一个数是否都是素数,就可以遍历整个区间的每一个整数,看看这个数除以除1和自己本身之外,是否还有别的因子,有的话就不是喽,然后再把筛到的素数打印就行喽:

//判断素数
#include <cstdio> 
//c = a * b
#include <cmath> 
#define N 10005 
using namespace std; 
//min(a , b) <= sqrt (c)
bool mindiv[N];//一个数字的最小素因子 
int prime[N]; 
int tot; 
bool judge(int x)//判断素数
{
    for(int i = 2 ; i <= sqrt(x) ; i ++)
    {
        if(x % i == 0)
        return false; 
    } 
    return true; 
} 

//Miller Rabin
int main()
{
    bool not_prime[10005]; 
    int n;
    scanf("%d",&n); 
    for(int i = 2 ; i <= n ; i ++)
    {
        if(judge(i))
            printf("%d ",i); 
    } //筛n以内的素数,打印出来,但复杂度有点大O(n) 
    puts(" ");
    for(int i = 2 ; i<= n ; i ++)
    { 
        if(! not_prime[i])    
        {
            printf("%d " ,i);
            for(int j = 2 ; j * i <= n ; j ++) 
            {
                not_prime[j * i] = true; 
            } 
        } 
    }//复杂度为nlogn,埃式拆法
     //n/2 + n/3 + n/5 +......
    puts(" ");
    for(int i = 2 ; i <= n ; i ++)
    {
        if(! mindiv[i])
        {
            mindiv[i] = i;
            prime[tot ++] = i;
            printf("%d ", i); 
        }
        for(int j = 0 ; prime[j] * i <= n ; j ++) 
        {
            mindiv[prime[j] * i] = prime[j]; // prime[j] <= mindiv[i] 
            //printf("# %d = %d * %d\n",prime[j] * i , i ,prime[j]); //输出判断i何时被筛掉 
            if(prime[j] == mindiv[i]) break; 
        } 
    } 
    //为何是线性 
    // 10 2 3 5 7
    return 0; 
} 

 

第一个是正常枚举筛。

 

第二个和第三个是埃式筛法和线性筛法。

转载于:https://www.cnblogs.com/Zhoier-Zxy/p/8434680.html

<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、付费专栏及课程。

余额充值