筛法--深入学习--1.欧拉筛

筛法是一种在数论和算法领域常用的算法策略,用于高效地找出一定范围内满足特定条件的数。最经典的筛法是埃拉托色尼筛法(Sieve of Eratosthenes),用于找出小于等于给定正整数 n 的所有质数。

筛法在密码学、数论计算、算法竞赛等领域有着广泛的应用,能够高效地解决与质数相关的各种问题。所以就让我来带领大家一起学习这把黄金钥匙吧。go~go~go~出发咯  

1.欧拉筛
 
- 欧拉筛,也叫线性筛,核心在于保证每个合数仅被其最小质因数筛去,从而实现线性时间复杂度 O(n)。
 
- 对于任意一个合数 N,它都可以唯一分解为 N = p_1^{a_1}p_2^{a_2}\cdots p_k^{a_k},其中 p_1\lt p_2\lt\cdots\lt p_k 为质数。欧拉筛就是利用每个合数必有最小质因数这一特性,通过从小到大遍历质数表和所有数,确保只在最小质因数处筛除该合数。
 
 C语言代码详细解析

#include <stdio.h>
#include <stdbool.h>

void eulerSieve(int n) {
    bool isPrime[n + 1];
    int prime[n];
    int primeCount = 0;

    // 初始化,假设所有数都是质数
    for (int i = 0; i <= n; i++) {
        isPrime[i] = true;
    }

    for (int i = 2; i <= n; i++) {
        if (isPrime[i]) {
            prime[primeCount++] = i;
        }
        for (int j = 0; j < primeCount && i * prime[j] <= n; j++) {
            isPrime[i * prime[j]] = false;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }

    // 输出所有找到的质数
    for (int i = 0; i < primeCount; i++) {
        printf("%d ", prime[i]);
    }
}

int main() {
    int n = 30;
    printf("小于等于 %d 的质数有: ", n);
    eulerSieve(n);
    return 0;
}
 


 
- 初始化部分:
 
-  bool isPrime[n + 1];  创建一个布尔数组  isPrime ,用于标记每个数是否为质数。
 
-  int prime[n];  创建一个数组  prime  来存储找到的质数。
 
-  int primeCount = 0;  初始化质数计数器为0。
 
- 通过循环将  isPrime  数组所有元素初始化为  true ,即假设所有数都是质数。
 
- 筛选部分:
 
- 外层循环  for (int i = 2; i <= n; i++)  从2开始遍历到 n。如果  isPrime[i]  为  true ,说明 i 是质数,将其存入  prime  数组,并增加质数计数器  primeCount 。
 
- 内层循环  for (int j = 0; j < primeCount && i * prime[j] <= n; j++)  遍历已找到的质数表。对于每个质数  prime[j] ,标记  i * prime[j]  为合数( isPrime[i * prime[j]] = false )。
 
- 当  i % prime[j] == 0  时, break  跳出内层循环。这是因为此时  i  是  prime[j]  的倍数,后续的  prime[j + 1] * i  等合数会在  i  增大到  i / prime[j] * prime[j + 1]  时,以  prime[j + 1]  为最小质因数被筛除,避免重复筛除。
 
- 输出部分:最后通过循环遍历  prime  数组,输出所有找到的质数。
 
优势与应用场景
 
- 优势:相比埃拉托色尼筛法,欧拉筛的时间复杂度为 O(n),效率更高,因为它避免了对合数的重复标记。在处理较大范围的数时,这种优势更加明显。
 
- 应用场景:
 
- 数论计算:在计算与质数相关的问题,如质因数分解、欧拉函数计算等方面有广泛应用。例如,计算一个数的欧拉函数 \varphi(n),需要先找出 n 的所有质因数,欧拉筛可以高效地提供所需的质数列表。
 
- 密码学:在一些密码算法中,质数的生成和筛选是重要的环节。欧拉筛的高效性有助于快速生成满足条件的大质数,保障密码系统的安全性。
 
- 算法竞赛:在竞赛题目中,涉及质数查找、素数相关性质计算等问题时,欧拉筛是常用的工具之一,可以帮助选手快速解决问题,提高解题效率。

学到这是不是感觉牛批的很呢,那我们看看模板题练练火热的手吧。

P3383 【模板】线性筛素数 

 

题目背景
本题已更新,从判断素数改为了查询第 k 小的素数。

提示:本题输入输出、运算数据量较大。

对于 C++ 语言,如果你使用 cin 来输入输出,建议使用 std::ios::sync_with_stdio(0) 来加速,同时使用 '\n' 换行输出。
对于 Java 语言,使用线性筛并且优化输入输出,也可以在规定时限内通过本题,但是时限可能较紧张。
对于 Python 语言,语言性能差异较大,需要使用到 numpy 库的数组以替代列表,且使用埃氏筛法,依然可以在合适的时间和内存消耗下通过本题。
题目描述
如题,给定一个范围 n,有 q 个询问,每次输出第 k 小的素数。

输入格式
第一行包含两个正整数 n,q,分别表示查询的范围和查询的个数。

接下来 q 行每行一个正整数 k,表示查询第 k 小的素数。

输出格式
输出 q 行,每行一个正整数表示答案。

输入输出样例
输入 #1复制
100 5
1
2
3
4
5
输出 #1复制
2
3
5
7
11
说明/提示
【数据范围】
对于 100% 的数据,n=10 
8
 ,1≤q≤10 
6
 ,保证查询的素数不大于 n。

Data by NaCly_Fish.

题解代码:

#include <cstdio>
#include <cstring>

bool isPrime[100000010];
//isPrime[i] == 1表示:i是素数
int Prime[6000010], cnt = 0;
//Prime存质数

void GetPrime(int n)//筛到n
{
	memset(isPrime, 1, sizeof(isPrime));
	//以“每个数都是素数”为初始状态,逐个删去
	isPrime[1] = 0;//1不是素数
	
	for(int i = 2; i <= n; i++)
	{
		if(isPrime[i])//没筛掉 
			Prime[++cnt] = i; //i成为下一个素数
			
		for(int j = 1; j <= cnt && i*Prime[j] <= n/*不超上限*/; j++) 
		{
        	//从Prime[1],即最小质数2开始,逐个枚举已知的质数,并期望Prime[j]是(i*Prime[j])的最小质因数
            //当然,i肯定比Prime[j]大,因为Prime[j]是在i之前得出的
			isPrime[i*Prime[j]] = 0;
            
			if(i % Prime[j] == 0)//i中也含有Prime[j]这个因子
				break; 
		}
	}
}

int main()
{
	int n, q;
	scanf("%d %d", &n, &q);
	GetPrime(n);
	while (q--)
	{
		int k;
		scanf("%d", &k);
		printf("%d\n", Prime[k]);
	}
	return 0;
}

 看到这里看来你已经学的差不多了,下一期我将给你们介绍另一种也很厉害的筛法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值