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程序實現優化詳解 (一) 試除法
常常有初學者詢問求解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的增大,耗時仍是很多,那麼咱們還有更好的方法嗎?