首页 > 其他分享 >素数的几种常见线性筛法

素数的几种常见线性筛法

时间:2025-01-07 19:32:34浏览次数:3  
标签:10 埃氏 筛法 int 合数 素数 线性 欧拉

目录

前言

一.遍历查找

二.埃氏筛法

三.欧拉筛法(终极版)

结语


前言

        前些天写了一个查找范围区间的素数个数的题目,有两组数据一直tle,所以就特此学习了一些素数算法,所以又写了一遍,一是为了让自己对代码的熟悉程度有提高,一方面也是积累自己的算法模板。

一.遍历查找

        对于素数的算法,最简单的就是遍历,但他只限于小范围,范围一大就会tle或者栈溢出,对于小范围的使用是最直接,最好理解的。由一下问题为例:

/*问题:输出范围为1到1e2内所有的素数的个数*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e2;
static int is_primes(int a)
{
	int flag = 1;
	for (int i = 2;i <= sqrt(a);i++)
	{
		if (a % i == 0)
		{
			flag = 0;
			break;
		}
	}
	return flag;
}
int main()
{
	int count = 0;
	double s = clock();
	for (int i = 2;i <= N;i++)
	{
		int flag = is_primes(i);
		if (flag)
		{
			count++;
		}
	}
	double c = clock();
	printf("%d\ntime=%.0lfms", count, c - s);
    return 0;
}

结果:

可以看到,如果范围比较小的时候,普通的遍历还是比较可以的,至少能做到在0ms以内,但我们只要稍微把范围扩大,例如从1到1e5,这时,输出时间大大增加,直接看数据觉得还好,但是和接下来讲的两种算法简直就是降维打击

这是输出结果:

二.埃氏筛法

        核心思想:埃氏筛的核心思路就是利用合数和素数的关系来排除大量数据,例如合数4,4可以拆成2*2,也就是两个素数的乘,所以合数可以拆成2个或多个素数的乘积,我们可以利用这一点进行筛除。我们举一个例子:

例如,求2-10之间的素数。我们先确定2是素数,所以2的倍数(除2以外)就一定不是素数,所以我们把4,6,8,10划去;接下来下一个没被划去的数为3,发现3是素数,所以划去6,9;再接下来就是4,但已经被划去了,所以是5,所以把10划去,接下来没有新的未划去的数,所以剩下的2,3,5,7就是这个范围内所有的素数了。

        知道了核心思路,我们该如何将代码真正实现呢?首先我们得知道哪些数是被我们排除的,所以我们可以利用标记数组,对素数和合数进行标记,令素数为0,合数为1,类似于哈希表的思想。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5;
int is_primes(int a)
{
	int flag = 1;
	for (int i = 2;i <= sqrt(a);i++)
	{
		if (a % i == 0)
		{
			flag = 0;
			break;
		}
	}
	return flag;
}
int pri[N+10];//素数 0;合数 1
int main()
{
	int count = 0;
	double s = clock();
	for (int i = 2;i <= sqrt(N);i++)
	{
	
		if (is_primes(i))
		{
			for (int j = 2 * i;j <= N;j += i)
			{
				pri[j] = 1;
			}
		}
	}
	for (int i = 2;i <= N;i++)
		if(!pri[i]) count++;
	double c = clock();
	printf("%d\ntime=%.lfms", count, c - s);
	return 0;
}

输出结果:

可以明显看到,速度快了不少,那我们再把数据扩大至1e7呢?结果会是多少?

其实这个代码还可以继续优化,将判断素数的范围再改一下,将sqrt(a)改成i<=a/i。我们再来看看时间会是多少?

时间基本控制在109ms范围,又快了一点点,那这是不是素数筛的完全形态呢?

当然不是,在之前的例子中,相信大家已经能感觉到,我们在筛数时有些数是被重复筛了的,例如6,10,都被2和5这两个素数筛了一遍,这样就形成了大量无用时间的开销,所以算法还可以优化。也就是下面要讲到的欧拉筛。但是在讲欧拉筛之前,我们再看看之前的代码

在这里面,if语句里真的还要判断是否为素数呢?假设i=7,说明i到7之前所有的素数都没有把7筛除,是不是就可以说明7就是素数了,并不需要再进入函数判断7是否为素数了。

所以,埃氏筛最后优化出来的代码应该是这样的:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e7;
int pri[N+10];//素数 0;合数 1
int main()
{
	int count = 0;
	double s = clock();
	for (int i = 2;i <= N/i;i++)
	{
	
		if (!pri[i])
		{
			for (int j = 2 * i;j <= N;j += i)
			{
				pri[j] = 1;
			}
		}
	}
	for (int i = 2;i <= N;i++)
		if(!pri[i]) count++;
	double c = clock();
	printf("%d\ntime=%.lfms", count, c - s);
	return 0;
}

三.欧拉筛法(终极版)

        欧拉筛就是在埃氏筛的基础上进行优化,保证每个数只被筛掉一次,大大减少代码运行时间。我们引用一个数组存放判断出来的素数,在循环中,对i和这个数组的所有元素判断,看是否可以整除,从而是否跳出循环,来保证每个数只被筛一遍。

代码奉上:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e7;
int pri[N+10];//素数 0;合数 1
int primes[N + 10], p = 0;
int main()
{
	int count = 0;
	double s = clock();
	for (int i = 2;i <= N;i++)
	{
	
		if (!pri[i])
		{
			primes[++p] = i;  
			count++;
		}
		for (int j = 1;primes[j]*i<=N && j <= p;j++)  //查找是否已经被筛过,如果之前的素数就已经筛过,就break
		{
			pri[primes[j] * i] = 1;
			if (i % primes[j] == 0) break;
		}
	}
	/*for (int i = 2;i <= N;i++)  
		if(!pri[i]) count++;*/
	double c = clock();
	printf("%d\ntime=%.0lfms", count, c - s);
	return 0;
}

同样是1e7的范围,我们来看看欧拉筛每次需要的时间是多少?

是不是有种被降维打击的感觉,相比较于埃氏筛的代码,运行时间差不多减少了一半!足以看出欧拉筛的强大(强大无需多言)

结语

        学会了这两种素数筛之后,相信你对素数的判断和统计已经不再害怕,甚至做到游刃有余,那我们离题目AC还会远吗?

标签:10,埃氏,筛法,int,合数,素数,线性,欧拉
From: https://blog.csdn.net/2403_88817808/article/details/144936618

相关文章

  • 如何理解拟合模型之最小二乘法(线性回归)
    一、定义:一种用于拟合模型的数学方法,目标是找到一组模型参数,使得模型的预测值与真实值之间的误差平方和最小。二、核心思想:通过最小化误差,让模型尽可能接近训练数据三、应用场景:在回归分析中,最小二乘法广泛用于寻找数据点的最佳拟合直线或曲线。例如:在线性回归中,最小二乘......
  • 【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
    目录......
  • 基于自抗扰控制器和线性误差反馈控制律(ADRC-LSEF)的控制系统simulink建模与仿真
    1.课题概述基于自抗扰控制器和线性误差反馈控制律(ADRC-LSEF)的控制系统simulink建模与仿真。 2.系统仿真结果  3.核心程序与模型版本:MATLAB2022a 4.系统原理简介      自抗扰控制器(ActiveDisturbanceRejectionController,ADRC)结合线性误差反馈控......
  • 线性代数9.矩阵的逆-分块矩阵
    9.矩阵的逆-分块矩阵9.1分块矩阵的加法设矩阵\(A、B均为m\timesn\)的矩阵,且A、B均按相同的方式划分为\(s\timest\)块,其中:\[A=\begin{bmatrix}A_{11}&...&A_{1t}\\&...&\\A_{s1}&...&A_{st}\\\end{bmatrix}\]\[B=\begin{bmatrix}B_{11}&...&B_......
  • 启航数据结构算法之雅舟,悠游C++智慧之旅——线性艺术:顺序表之细腻探索
    人无完人,持之以恒,方能见真我!!!共同进步!!文章目录一、线性表的概念二、顺序表1.概念与结构2.顺序表的分类静态顺序表动态顺序表三、顺序表的实现1.顺序表的结构2.顺序表的初始化和销毁初始化函数销毁函数3.顺序表的扩容4.顺序表的尾插和头插尾插函数头插函数5.顺序......
  • 线性代数8.矩阵的逆-相关性质&特殊矩阵&算法应用
    8.矩阵的逆8.1相关性质性质1:若矩阵A可逆,则\(A^{-1}\)也可逆:\[(A^{-1})^{-1}=A\]性质1的证明:\(A\cdotA^{-1}=E\)性质2:若矩阵A可逆,则\(\lambda\cdotA\)也可逆:\[(\lambda\cdotA)^{-1}=\frac{1}{\lambda}\cdotA^{-1}\]性质2的证明:\(\lambda\cdotA\cdot\fra......
  • 线性代数7.矩阵的逆-定义&定理
    7.矩阵的逆-定义和定理7.1逆矩阵的定义对于n阶矩阵A,存在一个n阶矩阵B,使:\[AB=BA=E\]则称矩阵A是可逆的。且B是A的逆矩阵,简称“逆阵”,记为:\[B=A^{-1}\]7.2对逆矩阵的理解若存在矩阵\(A_{n×n}\)、\(x_{n×1}\)、\(b_{n×1}\),使:\[b=Ax\]又存在矩阵\(B_{n×n}\),使:\[AB=E......
  • 【视觉SLAM:五、非线性优化】
    状态估计问题状态估计问题是SLAM、目标跟踪、机器人导航等领域的核心问题,其目标是通过测量数据估计系统的状态(例如位姿、速度等)。它通常通过优化方法进行求解。批量状态估计与最大后验估计批量状态估计批量状态估计是通过所有观测数据一次性优化所有状态的过程:......
  • 线性系统与非线性系统
    判断一个系统是线性还是非线性,主要依据系统是否满足叠加性和齐次性两大性质:叠加性:如果一个系统对两个不同的输入信号\(x_1(t)\)和\(x_2(t)\)的响应分别是\(y_1(t\))和\(y_2(t)\),那么当输入为\(x_1(t)\)+\(x_2(t)\)时,输出应该是\(y_1(t)+y_2(t)。\)齐次......
  • 02专升本数据结构笔记 第二章:线性表
    专升本数据结构笔记第二章:线性表阿洛学长笔记lovettz线性表任务一线性表的定义和基本操作(阿洛学长)一、线性表的定义线性表是由n(n≥0)个类型相同的数据元素a1,a2,…,an组成的有限序列,数据元素之间是一对一的关系,记作L=(a1,a2,…,ai-1,ai,ai+1,…,an)(由n(n≥0)个......