【OI】线性筛

如何查找一个范围内的所有素数?

可以是从1~n挨个判断n%i 是否 == 0,也可以从 1~sqr(n) 一个个判断。

相信你们也听说过埃氏筛法,是使用每一个数的倍数筛掉合数!但是!每一个合数要被筛多次!这就给了我们优化的可乘之机!

它叫做线性筛,顾名思义,时间复杂度是线性的。

我们都知道,线性的复杂度已经非常优秀了,接下来我们的目的就是如何让每一个合数只被筛一次。

下面记住这条线性筛的原理:

每个合数只被它的最小质因子筛一次

每个合数只被它的最小质因子筛一次

每个合数只被它的最小质因子筛一次

重要的事情说三遍!!!

 

 

#include<iostream>
#include<cstdio>

const int MaxN = 20025;

bool notp[MaxN];

int prime[MaxN];
int tot;

int main(){
	
	int x;
	scanf("%d",&x);
	
	for(int i = 2; i <= x; i++){
		if(notp[i] == 0){
			prime[++tot] = i;
			
		}
		
		for(int j = 1; j <= tot; j++){
			if(i * prime[j] > x)
				break;
				
			notp[i*prime[j]] = 1;
			
			if(i % prime[j] == 0)
				break;
			
			
		}
		
	}
	
	for(int i = 1; i <= tot; i++){
		printf("%d ",prime[i]);
	}
	
	return 0;
}  

  

看起来非常简短,对不对?

下面解释一下这段代码:

输入x,求0~x区间中的所有素数。

接下来,是2~x的枚举,为什么从2开始相信各位也都明白。

notp数组的意思是"is not a prime",不是一个质数,值为0时代表它是一个质数。(在开始我们已经把它默认设置为全为素数,尽管事实不是那样)

如果发现一个数"is not not a prime"(=="is a prime)就把它放到我们的素数数组里,至于为什么这么做稍后解答。(而且,我们显然可以看出这个数组具有单调递增的性质)

接下来我们就循环我们的素数数组,对于每个元素筛去它的质数倍。

相信 if ( i * prime[j] > x) break; 大家都理解,下面就是标记它是一个素数。

接下来就是重点!

线性筛全部的Knowledge都凝聚在这一句话!if ( i % prime[j] == 0 ) break;

它意味着,只有最小的质因数才能筛去其它的数。

因为,如果 i % prime[j] == 0 的话,这就是说 i 拥有一个最小的质因数 prime[j] ,原因有两条:①我们之前没有触发过 i % prime[j] == 0,那么就是说,之前的 prime[1~j]都不是i的任何因数。

② i % prime[j] == 0 意味着, i * prime[j] 等同于 prime[j] * i/prime[j] * prime[j] ,其中所有数均为整数,最小质因子就是 prime[j]。这违反了我们在一开始的原理:

每个合数只被它的最小质因子筛一次


而我们通过找规律发现,i * prime[j] 的最小质因子永远是素数序列中的prime[j]。

(例如,素数序列是 2,3,正在枚举4,4*2=8,最小质因子是2;素数序列是2,3,正在枚举3,2*3=6,3*3=9,2和3都是其中的最小质因子)

那么,接下来我们要注意一个事情。为什么 if( i % prime[j] == 0 ) break; 要放在 notp[i * prime[j]] = 1; 后面呢?难道不能放在它前面吗?

刚刚我解释过的原因①,在触发 if( i % prime[j] == 0 ) break; 意味着prime[j]是i最先遇到的质因子,并且我们知道某一个数的某个因子一定小于等于它本身,所以显而易见,我们的素数序列包括到i(无论i是否是质数)的所有质数(如果i是质数,那么包括i)。我们之前也说过这个质数序列是单调递增的,那么prime[j]就是 i 的最小质因子!

而我们之所以在筛完 i * prime[j] 之后才运行这个条件判断,是因为 i % prime[j] 第一次 == 0 的时候,prime[j]是i的最小质因子!如果这里不break,那么下一次 i % prime[k] == 0的时候,显而易见,prime[k]不是i的最小质因子,这违反了我们在开头给出的原理。为了避免某些合数将来被筛第二次,所以我们这里直接break,把筛掉剩下 i * prime[x] 的事情交给之后的质数序列去做。这样,就可以保证每个质数只被筛一次。

 

最后输出质数序列,相信各位都明白。

 

转载于:https://www.cnblogs.com/dudujerry/p/10624854.html

内容概要:本文详细探讨了基于MATLAB/SIMULINK的多载波无线通信系统仿真及性能分析,重点研究了以OFDM为代表的多载波技术。文章首先介绍了OFDM的基本原理和系统组成,随后通过仿真平台分析了不同调制方式的抗干扰性能、信道估计算法对系统性能的影响以及同步技术的实现与分析。文中提供了详细的MATLAB代码实现,涵盖OFDM系统的基本仿真、信道估计算法比较、同步算法实现和不同调制方式的性能比较。此外,还讨论了信道特征、OFDM关键技术、信道估计、同步技术和系统级仿真架构,并提出了未来的改进方向,如深度学习增强、混合波形设计和硬件加速方案。; 适合人群:具备无线通信基础知识,尤其是对OFDM技术有一定了解的研究人员和技术人员;从事无线通信系统设计与开发的工程师;高校通信工程专业的高年级本科生和研究生。; 使用场景及目标:①理解OFDM系统的工作原理及其在多径信道环境下的性能表现;②掌握MATLAB/SIMULINK在无线通信系统仿真中的应用;③评估不同调制方式、信道估计算法和同步算法的优劣;④为实际OFDM系统的设计和优化提供理论依据和技术支持。; 其他说明:本文不仅提供了详细的理论分析,还附带了大量的MATLAB代码示例,便于读者动手实践。建议读者在学习过程中结合代码进行调试和实验,以加深对OFDM技术的理解。此外,文中还涉及了一些最新的研究方向和技术趋势,如AI增强和毫米波通信,为读者提供了更广阔的视野。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值