任意一个正整数k,若k≥2,则k可以表示成若干个质数相乘的形式。
Eratosthenes筛法中,在枚举k的每一个质因子时,我们都计算了一次k,从而造成了冗余。因此在改进算法中,只利用k的最小质因子去计算一次k。
而在其基础上改进的Eular筛法,其伪代码为:
isPrime[] = true primeList = [] primeCount = 0 For i = 2 .. N If isPrime[i] Then primeCount = primeCount + 1 primeList[ primeCount ] = i End If For j = 1 .. primeCount If (i * primeList[j] > N) Then Break End If isPrime[ i * primeList[j] ] = false If (i % primeList[j]) Then Break End If End If End For
与Eratosthenes筛法不同的是,对于外层枚举i,无论i是质数,还是是合数,我们都会用i的倍数去筛。但在枚举的时候,我们只枚举i的质数倍。比如2i,3i,5i,...,而不去枚举4i,6i...,原因我们后面会讲到。
此外,在从小到大依次枚举质数p来计算i的倍数时,我们还需要检查i是否能够整除p。若i能够整除p,则停止枚举。
Eular筛法可以保证每个合数只会被枚举到一次,时间复杂度为O(n)。当N越大时,其相对于Eratosthenes筛法的优势也就越明显。
下面是实现代码:
int numPrime(int n){ bool isPrime[1000005]; for(int i = 0; i< 1000005; i++){ isPrime[i] = true; } int PrimeList[100000]; int Primecount = 0; for(int i = 2; i<= n; i++){ if(isPrime[i]){ Primecount++; PrimeList[Primecount] = i; } for(int j = 1; j<= Primecount; j++){ if(i*PrimeList[j] > n) break; isPrime[i*PrimeList[j]] = false; if(i%PrimeList[j] == 0 ) break; } } // for(int i = 1; i<= Primecount; i++) cout << PrimeList[i] << endl; return Primecount; }