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

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