良好的數據結構和算法知識是成爲一個更好的程序員的第一步。爲了延續這個傳統,今天我將分享一個有趣的算法,Eratosthenes算法的Sieve,它能夠用來生成質數直到給定的數字。在不少狀況下,你須要生成指定整數的全部素數,而最經常使用於生成素數的算法是Eratosthenes算法的 Sieve。可是不多有開發人員知道這個算法,特別是Java程序員,這主要是由於沒有進行足夠的競爭性編程。程序員
Eratosthenes Sieve of Eratosthenes是一種古希臘算法,用於查找給定數字以前的全部素數,以希臘著名數學家Eratosthenes的名字命名。他是第一個計算地球周長的人,也因研究曆法閏年而聞名。算法
素數是一個整數,它能夠被1整除也能夠像2 3 5這樣被自身整除,而不能被任何正整數整除。編程
換句話說,素數不具備1或其自身的因子。可使用此算法生成從1到100或任意最大值的素數。數組
在本文中,不只將學習Eratosthenes Sieve算法的工做原理,還將使用該算法生成素數,並驗證生成的全部數是否都是素數。數據結構
Eratosthenes算法的Sieve如何工做數據結構和算法
Eratosthenes算法的Sieve很是簡單。能夠經過指定的整數建立大於1的數組,以便該數組的索引表示存儲在其上的實際整數。而後從2開始,由於0和1不被認爲是素數。編程語言
素數要麼能被1整除要麼能被自身整除,它沒有其餘因數。由於2是素數,咱們把它標記爲素數而後劃掉全部倍數由於它們不是素數,爲何?由於素數不具備除1以外的任何因子,而且若是數字是2的倍數,則意味着它能夠被2整除,所以不是素數。單元測試
爲了越過全部乘以2的數字,咱們只是將數組計數器跳過2,這意味着2,4,6,8都是2的倍數,它們將被交叉。數組中的下一個數字是3,如今3也不能被任何人整除,所以它是素數,咱們將其標記爲素數,並再次劃掉全部3的倍數,由於它們不是素數。學習
爲了超過3的倍數,咱們跳過數組計數器3,這意味着3,9,12,15都是交叉的。如今,下一個數字是4但它已經越過,由於它是2的倍數因此咱們跳到下一個數字是5。測試
5是素數,因此咱們將它標記爲素數,並將全部5的倍數交叉,由於它們不會是素數,它們能夠被5整除。爲了跨越5的全部倍數,咱們只增長數組計數器按5,因此數字如10,15,20,25將被劃掉。
咱們繼續這個過程,直到達到給定數字的平方根,由於數組中的每一個倍數都有一個小於或等於給定數字的平方根的素數因子,因此咱們沒必要交叉出倍數大於該根的數字 例如,爲了找到1到100之間的全部素數,它足以檢查到10。
這是咱們的Java程序,它使用Java編程語言中的Eratosthenes算法Sieve實現生成素數的邏輯:
import org.junit.Test; import static org.junit.Assert.*; /** *此類使用如下內容生成達到給定限制的素數
public static int[] generatePrimeNumbersUpto(int limit){ if(limit < 2){ return new int[0];
}else{ uncrossIntegerUpto(limit); crossOutMultiples(); putUncrossedIntegersIntoPrimes(); return primes; } }
private static void uncrossIntegerUpto(int limit) { crossedOut = new Marker[limit + 1]; for(int i = 2; i<crossedOut.length; i++){ crossedOut[i] = Marker.UNCROSSED; }
}
private static void crossOutMultiples() { int iterationLimit = determineIterationLimit(); for (int i = 2; i<= iterationLimit; i++){ if(notCrossed(i)){ crossOutMultipleOf(i); } }
}
private static int determineIterationLimit() { //數組中的每一個倍數都有一個素數因子 //小於或等於平方根 //數組大小,咱們不須要越過 //大於根的倍數。 double iterationLimit = Math.sqrt(crossedOut.length); return (int) iterationLimit; }
private static boolean notCrossed(int i) { return crossedOut[i] == Marker.UNCROSSED; }
private static void crossOutMultipleOf(int i) { for(int multiple = 2*i; multiple < crossedOut.length; multiple += i){ crossedOut[multiple] = Marker.CROSSED; }
}
private static void putUncrossedIntegersIntoPrimes() { primes = new int[numberOfUncrossedIntegers()]; for(int j = 0, i = 2; i<crossedOut.length; i++){ if(notCrossed(i)){ primes[j++] = i; } }
} private static int numberOfUncrossedIntegers() { int count = 0; for(int i = 2; i<crossedOut.length; i++){ if(notCrossed(i)){ count++; } } return count; }
} 在Java中檢查Prime數的單元測試
下面是一些單元測試,用於檢查咱們的程序是否正常工做
import static org.junit.Assert.*; import org.junit.Test; /** * Junit測試用例來測試咱們的Eratosthenes Sieve算法
@Test public void testPrimes(){ int[]primeUptoZero = PrimeNumberGenerator.generatePrimeNumbersUpto(0); assertEquals(0, primeUptoZero.length);
int[] primeUptoTwo = PrimeNumberGenerator.generatePrimeNumbersUpto(2); assertEquals(1, primeUptoTwo.length); assertEquals(2, primeUptoTwo[0]);
int[] primeUptoThree = PrimeNumberGenerator.generatePrimeNumbersUpto(3); assertEquals(2, primeUptoThree.length); assertEquals(2, primeUptoThree[0]); assertEquals(3, primeUptoThree[1]);
int[]primeUptoHundred=PrimeNumberGenerator.generatePrimeNumbersUpto(100); assertEquals(25, primeUptoHundred.length); assertEquals(97, primeUptoHundred[24]); }
@Test public void testExhaustive(){ for(int i = 2; i<700; i++){ verifyPrimeList(PrimeNumberGenerator.generatePrimeNumbersUpto(i)); } } private void verifyPrimeList(int[] listOfPrimes) { for(int i = 0; i<listOfPrimes.length; i++){ verifyPrime(listOfPrimes[i]); } }
private void verifyPrime(int number) { for (int factor = 2; factor < number; factor++){ assertTrue(number%factor != 0); } } } 這是咱們單元測試的結果,所有經過了
Java中的素數生成算法- Eratosthenes的篩選實例