素數算法(小結)

Java算法——判斷素數 算法

public static boolean isPrimeNumber(int number){
     if(number<2)
         return false;
     for(int i=2;i<=Math.sqrt(number);i++){
         if(number%i==0&&number!=2)
             return false;
     }
     return true;
 } 編程

素數算法(二)函數

上次討論了簡單的素數斷定的算法,不過這個算法對於位數較大(通常小於108)的素數斷定就顯得至關力不從心了。好比在素數目前最普遍的應用領域-公共密鑰體系中,通常選擇的素數都是至關大的(一般在100位以上),若是採用上次的試除法來斷定,那麼可能要窮盡你一輩子的時間都還不夠。因此在通常的應用領域,人們採用的是Rabin-Miller檢驗法。下面是該檢驗法的算法: 學習

首先選擇一個代測的隨機數p,計算b,b是2整除p-1的次數。而後計算m,使得n=1+(2^b)m。測試

(1) 選擇一個小於p的隨機數a。
(2) 設j=0且z=a^m mod p
(3) 若是z=1或z=p-1,那麼p經過測試,可能使素數
(4) 若是j>0且z=1, 那麼p不是素數
(5) 設j=j+1。若是j<b且z<>p-1,設z=z^2 mod p,而後回到(4)。若是z=p-1,那麼p經過測試,可能爲素數。
(6) 若是j=b 且z<>p-1,不是素數優化

數a被當成證據的機率爲75%。這意味着當迭代次數爲t時,它產生一個假的素數所花費的時間不超過1/4^t。實際上,對大多數隨機數,幾乎99.99%確定a是證據。spa

實際考慮:.net

在實際算法,產生素數是很快的。設計

(1) 產生一個n-位的隨機數p
(2) 設高位和低位爲1(設高位是爲了保證位數,設低位是爲了保證位奇數)
(3) 檢查以確保p不能被任何小素數整除:如3,5,7,11等等。有效的方法是測試小於2000的素數。使用字輪方法更快
(4) 對某隨機數a運行Rabin-Miller檢測,若是p經過,則另外產生一個隨機數a,在測試。選取較小的a值,以保證速度。作5次 Rabin-Miller測試若是p在其中失敗,重新產生p,再測試。rest

經測試,這個算法在sun的Sparc II工做站上實現:
2 .8秒產生一個256位的素數
24.0秒產生一個512位的素數
2分鐘產生一個768位的素數
5.1分鐘產生一個1024位的素數


最近在網上看了很多關於素數的問題,也學習到了很多東西,決定整理一下,算是一個學習的總結吧。

首先想說明的是,雖然素數能夠進行很深刻的研究(如在RSA公共密鑰系統的應用),可是因爲我對數論的不甚熟悉,因此只能作一些淺嘗輒止的探討,主要就是對一些簡單的素數相關算法進行一個討論。

首先來講說素數的斷定算法,若是你是讀譚浩強老師的《c程序設計》入門的話,那麼一談到素數的斷定算法,你首先應該想到的就是如下的算法:給定一個正整數n,用2到sqrt(n)之間的全部整數去除n,若是能夠整除,則n不是素數,若是不能夠整除,則n就是素數。這個算法的時間複雜度十分明瞭,爲O(sqrt(n)),算法的描述至關簡單,實現也同樣不困難。

# include <stdio.h>
# include <math.h>

int isPrime(int n)
{
    int i ;
 
    for(i=2; i <= sqrt(n); i++){
        if(n%i == 0 )
            break ;
    }

    if(i <= sqrt(n))
        printf("%d is not a prime ! ", &n) ;
    else
        printf("%d is a prime ! ", &n) ;
 
    return 0 ;
}


=====================================
public class  SuShu{  
 private int num; 
 SuShu(int n){   
  num=n;
 } 
 public  boolean isSuShu(){
  for(int i=2;i<num;i++){
   if(num%i==0)
    return false;                
  }    
   return true; 
 } 
 public static void main(String[] args){   
  for(int i=1;i<=100;i++){
   SuShu su=new SuShu(i);
   if(su.isSuShu())
    System.out.println(i+"是素數");
   else
    System.out.println(i+"不是素數");   
  }
 }
}

=============================

/**  
* @param n  
* @return if n is a prime return true, else false  
*/ 
public static boolean isPrime(int n) {
 // filte negative, zero, one   
 if (1 >= n) {
       return false;
 }
 // 2 is a prime, stop filter   
 if (2 == n) {
  return true;
 }
 // filter evens
 if (0 == n % 2) {
  return false;
 }
 // go on filting...   
 for (int a = 3; a <= Math.sqrt(n); a += 2) {
  if (0 == n % a) {
   return false;
  }
 }
 // the rest is all prime, stop filting   
 return true;
}
==============================
//目前我認爲最好的辦法是:(不是lk的觀點)
public boolean isPrime(int n){
    for(int i = 2; i * i <= n; i++){
  if(n % i == 0)
   return false;
  }
    return true;
}
===============================
素數是這樣的整數,它除了能表示爲它本身和1的乘積之外,不能表示爲任何其它兩個整數的乘積。例如,15=3*5,因此15不是素數;又如,12=6*2=4*3,因此12也不是素數。另外一方面,13除了等於13*1之外,不能表示爲其它任何兩個整數的乘積,因此13是一個素數。
有的數,若是單憑印象去捉摸,是沒法肯定它究竟是不是素數的。有些數則能夠立刻說出它不是素數。一個數,無論它有多大,只要它的個位數是二、四、五、六、8或0,就不多是素數。此外,一個數的各位數字之和要是能夠被3整除的話,它也不多是素數。但若是它的個位數是一、三、7或9,並且它的各位數字之和不能被3整除,那麼,它就多是素數(但也可能不是素數)。沒有任何現成的公式能夠告訴你一個數究竟是不是素數。你只能試試看能不能將這
個數表示爲兩個比它小的數的乘積。

代碼以下:

package com.vo;

public class Sushu {


 public static void main(String[] args) {
  int s=0;
  int i;
  for(i=0;i<=100;i++)
  {
 int j;
 for(j=2;j<=i;j++){
  if(i%j==0)
   break;
 }
 if(i==j)
  System.out.println(i);
  }

 }

}

----------------------------------------------------------------------------------

素數算法大全,及C程序實現優化詳解 () 試除法

發佈日期: 2009-05-04   來源: Doforfun.net   做者:三藏法師

常常有初學者詢問求解N內全部素數(質數)的問題,對此,網上的解答也不少,但不少要麼不夠專業,要麼只有程序沒有算法解析,因此三藏大廈對此問題作個小結,探討一下求解素數的常見算法,同時給出相應的C語言程序及其解析。爲了方便初學者理解,本文將從易到難闡述不一樣算法,高手能夠直接看後面的高效算法
質數的定義
一個數,若是隻有1和它自己兩個因數,這樣的數叫作質數,又稱素數。

試除判斷法
算法描述:從上述定義可知,素數不能被1和它自己以外的數整除,因此,判斷一個數x是否素數只要看它是否能被2~sqrt(x)間的數整除便可;而求N內全部素數則是循環重複上述過程。
C語言實現

複製內容到剪貼板

代碼:

#include <time.h>
#include <malloc.h>
#define N 100000
//
簡單試除判斷法 Ver1
int s i m p l eDivisionV1(int n)
{
int i,j;
//
素數數量統計
int count = 0;
//
分配存放結果的空間
int* primes = (int*)malloc( sizeof(int)*n );

// 2
是素數誰都知道,不算了
primes[count++] = 2;
//
循環計算3~n間的數
for (i=3; i<=n; i++)
{
  //
爲何是sqrt(i),思考一下
  for (j=2; j<=sqrt(i); j++)
  {
   // i
j整除,顯然不是素數了
   if (i%j == 0) break;
  }
  // i
不能被2~sqrt(i)間的數整除,素數也
  if (j > sqrt(i))
  {
   primes[count++] = i;
  }
}

//
因輸出費時,且和算法核心相關不大,故略
  
//
釋放內存,別忘了傳說中的內存泄漏
free(primes);

return count;
}

void main()
{
int count;
clock_t start, end;
// time
函數不夠精確,用clock湊合一下吧
start = clock();
count = s i m p l eDivisionV1(N);

end = clock();
printf("[%d]
之內素數個數:%d, 計算用時:%d毫秒\n", N, count, end-start);
getch();
}

計算結果:
[100000]
之內素數個數:9592, 計算用時:468毫秒
[1000000]
之內素數個數:78498, 計算用時:10859毫秒
[5000000]
之內素數個數:348513, 計算用時:103560毫秒
噢噢,算算十萬還行,百萬就10秒多了,並且時間增加很快,這不行,得優化一下!
優化分析
仔細研究一下s i m p l eDivisionV1咱們能夠發現如下幾個問題:

1.     在循環條件中重複調用sqrt(i)顯然是比較浪費時間的

2.     判斷素數,真的須要拿2~sqrt(i)間的全部整數去除嗎?咱們知道,合數均可以分解成若干質數,因此只要2~sqrt(i)間的質數不能整除i便可

根據上面兩點,咱們可將s i m p l eDivisionV1升級爲s i m p l eDivisionV2,以下

複製內容到剪貼板

代碼:

// 簡單試除判斷法 Ver2
int s i m p l eDivisionV2(int n)
{
int i, j, k, stop;
//
素數數量統計
int count = 0;
//
分配存放結果的空間
int* primes = (int*)malloc( sizeof(int)*n );

// 2
是素數誰都知道,不算了
primes[count++] = 2;
stop = count;
//
循環計算3~n間的數
for (i=3; i<=n; i++)
{
  k = sqrt(i);
  //
在循環條件中重複調用sqrt是低效作法,故引入k
  while (primes[stop] <= k && stop < count)
   stop++;
  // stop
幹什麼用,思考一下
  for (j=0; j<stop; j++)
  {
   if (i%primes[j] == 0) break;
  }
  // i
不能被2~sqrt(i)間的素數整除,天然也不能被其餘數整除,素數也
  if (j == stop)
  {
   primes[count++] = i;
  }
}

//
因輸出費時,且和算法核心相關不大,故略
  
//
釋放內存,別忘了傳說中的內存泄漏
free(primes);

return count;
}

而後將main中調用的函數替換爲s i m p l eDivisionV2,在看一下執行結果:
[100000]
之內素數個數:9592, 計算用時:46毫秒
[1000000]
之內素數個數:78498, 計算用時:546毫秒
[5000000]
之內素數個數:348513, 計算用時:3515毫秒
[10000000]
之內素數個數:664579, 計算用時:8000毫秒
很開心的看到,通過優化,速度提升了幾十倍,尤爲是時間增加曲線的坡度變小了,N值越大,V2函數比V1的效率就越高
對於試除判斷這種質數算法來講,三藏認爲s i m p l eDivisionV2基本已經接近極限,不大可能有量級上的突破了,有興趣的朋友能夠本身進一步優化。初學者除了參看上述例子外,能夠嘗試作各類修改及細節優化,也能夠將除法變乘法,多加練習是學習編程的好方法。
雖然,上例中V2已經比V1快了不少了,但隨着N的增大,耗時仍是很多,那麼咱們還有更好的方法嗎?

相關文章
相關標籤/搜索