程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 快速求1000,000范圍內的所有素數,復雜度為O(n)

快速求1000,000范圍內的所有素數,復雜度為O(n)

編輯:C++入門知識

今天收獲不小啊,自創了一個快速O(n)復雜度的求1 ---- 1000,000范圍內的所有素數

注:這個方法確實是自創,如果有人想轉,請注明本文來處,否則我會很不爽的

思路:首先定義一個isPrime[1000001]的數組,初始化為0;isPrime[i] = 0表示i還沒有確定是否是素數,isPrime[i] = 1表示i一定不是素數

             對於一個數,他一定是由某些素數相乘得到的,那麼假如我們得到了一個x為素數,那麼可以利用x去掉比x大的是x的倍數的數

           但是這樣的話還是不夠,因為x從2到1000,000的遍歷,x的倍數也要從x往1000,000遍歷,鐵定不行

這個時候,我們可以發現連個可以優化的地方(下面講“判定”這個詞的時候,意思就是判定是否為素數)(顯然,當x遍歷到一個isPrime【x】==0的時候,說明x沒能被比x小的素數判定為合數,x肯定沒有比x大的因子,所以x肯定是素數)

          (1),x的倍數x*y,判定x*y的時候,y不必比x小,因為如果y比x小,那麼x*y一定在之前已經被判定了;如何解釋:分兩種情況,如果y是素數,那麼x*y已經被y判定過,如果y不是素數,那麼x*y一定被y的因子判定過,這時候x*y不用判定

 

          (2),x的倍數x*y,y一定不是已經被判定為isPrime【y】 = 1,即y一定不是素數,也就是說y是合數,那麼這時候y的因子肯定小於或者等於x(注意有等於的情況,後面解釋等於的情況要特殊處理),因為y已經被判定了,這個因子也一定是x*y的因子,那麼x*y也已經被判定為不是素數了

 

          談優化,我們可以把所有的2—1000,000用一個雙向鏈表存起來,用where[i]表示i的鏈表中的節點地址,當i被判定為不是素數時就可以把i從鏈表中刪除,利用where就可以不用遍歷,O(1)的時間刪除

         每次得到一個素數,遍歷鏈表,得到判定為合數的數,刪除這個數剪短鏈表直到這個數大於1000,000就跳出遍歷,那麼每遍歷一個節點就刪除一個節點,這個算法的復雜度可以理解為1000,000長度的鏈表在O(n)次的操作中遍歷多少節點就刪除多少節點,總的復雜度當然就是O(n)了

 

      特別注意,我們不能從素數x判定到一個數z為合數就把他從鏈表中刪除,為什麼,因為z的所有因子都是x,那麼刪除了z,那麼z*x就沒法判定了,對於這種情況,我們可以在利用了z之後刪除z,而那些沒法再利用的,也就是z*x大於1000,000的就直接從鏈表中刪除

 

還有注意一點就是int溢出,所以我們可以定義一個int64的變量存相乘的值

 

模版代碼:注釋部分是我測試的算法復雜度,對於求1000,000以內的素數,只用了120,0000步

#include<stdio.h>
#include<string.h>
#define MAX 1000000
typedef __int64 LL;

struct List
{
 List *last;
 int value;
 List *next;
};

bool isPrime[MAX + 1];

List* where[MAX + 1];

int isResult[MAX + 1];

int init()
{
 int i;
 LL temp, temp1;
 List *p;
 List *p1;
 List *q1;
 List *head;
// int step = 0;                                              //測試用了多少步

 memset(isPrime, 0, sizeof(isPrime));


 for(i = 2; i <= MAX; i ++)
 {
  p = new List;
  where[i] = p;
  if(i == 2)
  {
   p -> last = NULL;
   p -> value = i;
   p -> next = NULL;
   head = p;
  }
  else
  {
   p -> last = where[i - 1];
   where[i - 1] -> next = p;

   p -> value = i;
   p -> next = NULL;
  }
 }

 for(i = 2; i <= MAX; i++)
 {
  if(isPrime[i])
   continue;
  else
  {
   for(p = head; p != NULL; p = p -> next)
   {
//    step ++;//測試用了多少步
    temp = i * (LL)(p -> value);
    if(isPrime[p -> value] && where[p -> value] != NULL)
    {
     temp1 = p -> value;
     p1 = where[temp1] -> last;
     q1 = where[temp1] -> next;

     p1 -> next = q1;
     if(q1 != NULL)
      q1 -> last = p1;

     delete where[temp1];
     where[temp1] = NULL;
     p = p1;
    }
    if(temp > MAX)
     break;
    if(!isPrime[temp])
    {
     isPrime[temp] = 1;
     if(temp * i > MAX)
     {
      p1 = where[temp] -> last;
      q1 = where[temp] -> next;
      delete where[temp];
      where[temp] = NULL;

      p1 -> next = q1;
      if(q1 != NULL)
       q1 -> last = p1;
     }

    }
   
   }
  }
 }

// printf("%d\n", step);//測試用了多少步

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved