2017年蓝桥杯《等差素数列》解析+素数筛浅谈

写在前面:好久没有没有更新博客了,新复习了素数筛的方法之一埃式筛法,简述一下这个算法的基本原理,然后分析一下这个蓝桥杯的水题。

埃式筛法原理剖析:

我们在日常编程中经常会遇到有些题目让你求一个区间内的素数,针对这个问题许多脑回路清晰的大佬提出了各种各样的筛素数的方法,或许我们觉得这还不简单,我猜大部分人都会这样筛

bool Is_prime(int n){
for(int i=1;i*i<=n;++i){
    if(n%i==0) return false;
    }
return true;
}

这样筛没毛病嗷,但是我们在运行大数据的测试时会显得非常拖后腿,所以说我们需要优化时间复杂度。由此可以引出我们所要详细讲述的筛法——埃式筛
我们先来看一下代码

void isprime(int n){
for(int i=2;i<=n;i++)
{
    if(!vis[i]) prime[x++]=i;
    for(int j=2;j*i<=n;j++){
        vis[i*j]=true;
        }
    }
}

这一下脑瓜子嗡嗡的了吧
我们先来解释一下第一个循环,之所以为什么i从2开始,是因为最小的素数是2,n是咱们任意给定的一个结尾,这个vis数组是用来依次存放2到n的所有数,(由于丹某忘了数组是个啥玩意,这里给出一个通俗的解释:变量只代表一个数,而数组可以存放多个数,我们来设一个数组a[1000],你以为这能存1000个数?错!能存1001个,因为下标是从0开始,即a[0],a[1],a[2].....a[1000],每一个都可以存一个数,这样大大节省你定义的时间,省时又好用),回归正题,我们用这个if,用来判断是否为素数,如果是那么prime数组就记录下来这个素数的下标是多少,x在这里充当计数器的作用,接下来的这个循环是用来提高效率的,其原理是当我们判断出当前这个数是个素数那么我们就能知道这个数的倍数一定不是个素数,那么ij就代表了当前这个数的倍数,我们之前知道vis是用来存放所有数的,那么vis[ij]就是当前判断出来的素数的倍数统统打上标记,以便接下来的判断遇到这个数的倍数就直接跳过省的再判断一次了,所以说我们就降低了复杂度,现在复杂度是O(nlog(logn));
通俗来讲:首先将2到n范围内的整数写下来,其中2是最小的素数。将表中所有的2的倍数划去,表中剩下的最小的数字就是3,他不能被更小的数整除,所以3是素数。再将表中所有的3的倍数划去……以此类推,如果表中剩余的最小的数是m,那么m就是素数。然后将表中所有m的倍数划去,像这样反复操作,就能依次枚举n以内的素数。

还有以一种复杂度更低的方法,那就是欧拉筛,复杂度O(n)
他比埃式筛的优化在于每个合数只被它的最小质因子筛选一次,以达到不重复的目的
代码如下:

void oulasai(int n){
for(int i=2;i<=n;i++){
    if(!vis[i]) prime[x++]=i;
    for(int j=0;j<x;j++){
        if(i*prime[j]>n) break;
        vis[i*prime[j]]=true;
        if(i%prime[j]==0) break;
        }
    }
}

这个就不做详细说明了(本蒟蒻菜还没找到合适解释

就题论题

Question

enter image description here

Solution

首先我们用上面的的筛素数的方法写一个筛素数的分函数
主函数里的主要作用是用来寻找长度为10且公差值最小的等差素数列
首先我们写一个循环用来从最小公差2开始寻找一个素数后面的等差10项素数
我们从最小公差开始寻找的目的是保证我们找到的符合条件的数列公差一定是最小
然后我们下一个循环用来循环首项,即从第一个素数开始寻找符合条件的数列
里面开一个计数器用来记录数列的长度
然后我们写一个从0-9的循环用来寻找当前素数往后10个素数是否等差,每符合一次计数器+1
当我们的计数器第一次到了10,证明我们寻找到了符合条件的数列那么,我们输出当前循环到的公差
即为最小公差。

Code

#include<bits/stdc++.h>
using namespace std;
int a[1000010]={1,1};
int isprime(int n){//埃式筛 
for(int i=2;i<=n;i++){
	if(!a[i])for(int j=2;j*i<=n;j++)a[i*j]=1;
	}
}
int main(){
isprime(1000000);
for(int d=2;d<=10000;++++d){// 公差 
	for(int i=1;i<=100000;++i){//首项 
		int l=0;
		for(int j=0;j<=9;++j){
			if(!a[i+j*d])++l;//判断当前素数后面的等差数是否符合素数 
			if(l==10){
				printf("%d\n",d);
				return 0;
				}
			}
		
		}
	}
} 

完结,撒花!写于2022.1.15 0:16

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章