以筛出100以内(含100)的所有素数为例来说明一下欧拉筛法的原理。
和Eratosthenes筛法一样,Euler筛法也从2开始筛,但Eratosthenes筛法会把2的倍数一批全部筛掉,而Euler筛法用2筛时仅仅把2*2(即4)筛掉,而把其它偶数留到后面再筛掉,从而避免了一个偶数被多次筛除带来的性能开销,比如偶数6、8、10,在随后用3、4、5筛的时候会被筛掉。
Euler筛法引入了一个素数队列,从2开始没被筛掉的数会依次加进素数队列,当用3筛时,素数队列里有2和3两个素数,这时会筛掉3*2(即6)和3*3(即9)。
当用4筛时,4因为之前被2筛掉了,所以4不会加进素数队列,此时素数队列里依然只有2和3两个素数,这时会筛掉4*2(即8),但4*3(即12)并不在这时筛掉,这是因为4本身是素数2的倍数,即4*3里不能保证3是最小素因数。
当用5筛时,先把5加入素数队列,随后筛掉5*2(即10)、5*3(即15)和5*5(即25)。
当用6筛时,6因为之前被3筛掉了,所以此时素数队列里依然只有2、3、5,这时会筛掉6*2(即12),但因为判断出6是2的倍数,随后就不会用6进一步去筛掉6*3(即18)。
当用7筛时,先把7加入素数队列,随后依次筛掉7*2(即14)、7*3(即21)、7*5(即35)和7*7(即49)。
当用8筛时,8因为之前被4筛掉了,所以此时素数队列里依然只有2、3、5、7,这时会筛掉8*2(即16),但因为判断出8是2的倍数,随后就不会用8进一步去筛掉8*3(即24)。
当用9筛时,9因为之前被3筛掉了,所以此时素数队列里依然只有2、3、5、7,这时会筛掉9*2(即18)和9*3(即27),但因为判断出9是3的倍数,随后就不会用9进一步去筛掉9*5(即45)。
当用10筛时,10因为之前被5筛掉了,所有此时素数队列里依然是2、3、5、7,这时会筛掉10*2(即20),但因为10是2的倍数,随后不会用10去筛掉10*3(即30)。
当用11筛时,先把11加入素数队列,随后依次筛掉11*2(即22),11*3(即33),11*5(即55),11*7(即77)。11*11(即121)因为大于100而不在筛查范围。
用12筛,只会筛去12*2。
用13筛,13进素数队列,随后依次筛去13*2,13*3,13*5,13*7。13*11不在筛查范围。
用14、15、16筛,依次会筛去14*2、15*2和15*3、16*2。
用17筛,17进素数队列,随后依次筛去17*2、17*3、17*5。17*7不在筛查范围。
……
用49筛,会筛去49*2。49*3不在筛查范围。
用50筛,会筛去50*2。50*3不在筛查范围。
用51筛,不会筛去任何数,因为51*2>100。
随后用52、53、……、99、100筛的过程里,都不再会筛去任何数,而只有素数进素数队列的行为。
由上述过程可知,欧拉筛法的核心思路是:对于筛查范围内的任意一个合数m,只使用m中除m之外最大的因数把m筛去。例如,45=3*3*5,确保用15筛去45即可。更一般的情形,m=p1*p2*……*ps,其中p1、p2、……、ps均为素数且满足p1<=p2<=……<=ps,则确保用p2*……*ps筛去m即可。
1 typedef unsigned char u8; 2 typedef unsigned long ulong; 3 static ulong s_last = 0; 4 static u8* s_pAll = NULL; 5 static std::vector<ulong> s_vecPrime;
和Eratosthenes筛法实现的定义完全一样。
1 int main() 2 { 3 printf(" EulerSieve: a method to find out all primes below the number that you specify here please: "); 4 std::string strInput; 5 getline(std::cin, strInput); 6 s_last = strtoul(strInput.c_str(), 0, 10); 7 if (s_last <= 2) { 8 printf("\n Wrong input.\n"); 9 return 0; 10 } 11 printf("\n Only the sum of all primes needed [y/n](y as default): "); 12 getline(std::cin, strInput); 13 bool bDetail = (strInput == "n"); 14 if (bDetail) 15 printf("\n Start to work out all primes below %u...\n", s_last); 16 else 17 printf("\n Start to work out the sum of all primes below %u...\n", s_last); 18 if (!eulerSieve()) 19 return 0; 20 if (bDetail) 21 showDetails(); 22 return 0; 23 }
1 bool eulerSieve() 2 { 3 DWORD tickBegin = GetTickCount(); 4 s_pAll = new u8[s_last]; 5 if (!s_pAll) { 6 printf("Lack of memory.\n"); 7 return false; 8 } 9 10 ulong sum = 0; 11 memset(s_pAll, 1, s_last); 12 for (ulong num = 2; num < s_last; ++num) { 13 if (s_pAll[num - 1] == 1) { 14 ++sum; 15 s_vecPrime.push_back(num); 16 } 17 for (ulong idx = 0; idx < sum; ++idx) { 18 ulong prime = s_vecPrime[idx]; 19 ulong multiple = num * prime; 20 if (multiple >= s_last) 21 break; 22 s_pAll[multiple - 1] = 0; 23 if (num % prime == 0) 24 break; 25 } 26 } 27 printf(" %u primes found in %u milliseconds.\n\n", sum, GetTickCount() - tickBegin); 28 delete []s_pAll; 29 return true; 30 }
showDetails函数实现和Eratosthenes筛法里的实现完全一样。
PS H:\Read\num\x64\Release> .\eulerSieve
EulerSieve: a method to find out all primes below the number that you specify here please: 1234567890
Only the sum of all primes needed [y/n](y as default):
Start to work out the sum of all primes below 1234567890...
62106578 primes found in 15734 milliseconds.
PS H:\Read\num\x64\Release> .\eulerSieve
EulerSieve: a method to find out all primes below the number that you specify here please: 123456789
Only the sum of all primes needed [y/n](y as default):
Start to work out the sum of all primes below 123456789...
7027260 primes found in 1391 milliseconds.
PS H:\Read\num\x64\Release> .\eulerSieve
EulerSieve: a method to find out all primes below the number that you specify here please: 12345678
Only the sum of all primes needed [y/n](y as default):
Start to work out the sum of all primes below 12345678...
809227 primes found in 141 milliseconds.
PS H:\Read\num\x64\Release> .\eulerSieve
EulerSieve: a method to find out all primes below the number that you specify here please: 100
Only the sum of all primes needed [y/n](y as default): n
Start to work out all primes below 100...
25 primes found in 0 milliseconds.
2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97
PS H:\Read\num\x64\Release>
对比用C++实现的Eratosthenes筛法程序里的eSieve和用C++实现的增强Eratosthenes筛法程序里的eSievePro,eulerSieve的性能优于eSieve,但不如eSievePro。
https://github.com/readalps/EulerSieve上放了EulerSieve实现的源码文件,以及一个运行结果文件。
原文:https://www.cnblogs.com/readalps/p/14294618.html