三種素數篩的比較

在自然數集中,質數的數量不多而且分佈比較稀疏,對於一個整數N,不超過N的質數大概有N/lnN個,即每lnN個數中可能會有一個質數。

在篩選0—N中的素數的方法較多:

1.試除法

bool is_prime(int n)
{
    for(int i=2;i<=sqrt(n);i++)
         if(n%i==0)return false;
   return true;
}

2.Eratosthenes篩選法

想法是:任意整數x的倍數 2x,3x,..都不會是質數

我們從2開始掃,從小到大掃每個數x,把他的倍數2x,3x,...,[N/x]*x標記爲合數.

掃到一個數時,它不被2—x-1之間任何數整除,該數即爲質數

當然,我們會發現,2和3都會把6標記爲合數。

由此我們可知,小於x^2的x的倍數在掃更小的數時已經被掃了一遍。

就不必像2和3都會把6掃一遍那樣多餘的白白浪費運算時間了。

我們要做的就是對這個算法進行優化

:對於每個數x,我們只需從x^2開始掃,把x^2,(x+1)*x,...,[N.x]*x標記合數即可。

void primes(int n)
{
     memset(v,0,sizeof(v));//標記合數
     for(int i=2;i<=n;i++)
     {
          if(v[i])continue;
          else cout<<i<<endl;//看是不是質數,是質數的話輸出
          for(int j=i;j<=n/i;j++)v[i*j]=1;
     }
}

當然這篇blog到此不會截止,我想說的是:雖然.Eratosthenes篩選法是讓素數x從x^2往上開始2篩的,但還是會造成重複篩選。

就像2和3都會把6標記爲合數一樣,

雖然Eratosthenes篩選法是把x^2,(x+1)*x,...,[N.x]*x標記合數

但還是會造成重複篩選。

如:12=6*2,12=4*3,很明顯12被重複篩選了,

Eratosthenes篩選法 的本質和爆破的試除法 一樣,只不過減少了重複篩選的次數。

而我們想知道的是產生一個合數的唯一方式。

這時我們講一下線性篩:

舉個簡單的例子:我們通過 從小到大累積質因子 來標記每個合數,讓12=3*2*2是合數組成的唯一的方式

線性篩是通過 從小到大累積質因子 來標記每個合數,當我們理解這句話的含義時,實現代碼就不難了

int v[maxn],prime[maxn];//prime用來記載質數
void primes(int n)
{
       memset(v,0,sizeof(v));
       m=0;//最小質因子
       for(int i=2;i<=n;i++)
       {
            if(v[i]==0)
             { v[i]=i;prime[++m]=i; }//i是質數的話

           //讓當前數i乘上一個質因子
            for( int j=1;j<=m;j++)
             {
                 //i有比prime[j]更小的質因子  或超出n的範圍停止
                  if(prime[j]>v[i] ||prime[j]>n/i )break;
                 //prime[j]是合數i*prime[j]的最小質因子
                  v[i*prime[j]]=prime[j];
             }
       }
            for(int i=1;i<=m;i++)cout<<prime[i]<<endl;
}

這種方法省時就省時在,不會造成重複篩選!

其實關於質數的題目還有一個應用就是:質因數分解

pi序列都爲質數,ci爲次冪

對任何N,都有:

N=p1^c1*p2^c2.......

由此可以利用 試除法 和 Eratosthenes篩選法 完成質因數分解:

其實 它的一個更好的應用是求最大質因子 因爲一個數字不可能有兩個大於根號的因子,還是素因子所以我們函數內,for循環的條件是:

for(int i=2;i<=sqrt(n);i++)

#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
const int maxn=1e5;
long long p[maxn];
long long c[maxn];
void divide(int n)
{
        int m=0;
        for(int i=2;i<=sqrt(n);i++)
        {
                   if(n%i==0)//i爲質數
                  {
                           p[++m]=i;c[m]=0;
                          while(n%i==0)
                           {
                                   n/=i;c[m]++;
                           }
                  }
        }   //最後剩餘的數不能爲任何區間內質數整除,則剩餘的數              也爲質數
            if(n>1)
            {p[++m]=n;c[m]=1;   }

       for(int i=1;i<=m;i++)
            cout<<p[i]<<" "<<c[i]<<endl;
}
int main()
{
    int n;cin>>n;
    divide(n);
    return 0;
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章