首页 技术 正文
技术 2022年11月6日
0 收藏 684 点赞 582 浏览 4806 个字

运行示例

PS H:\Read\num\x64\Release> .\eSievePro
Eratosthenes sieve: 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> .\eSievePro
Eratosthenes sieve: 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 63 milliseconds.

PS H:\Read\num\x64\Release> .\eSievePro
Eratosthenes sieve: 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 1031 milliseconds.

PS H:\Read\num\x64\Release> .\eSievePro
Eratosthenes sieve: 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 12375 milliseconds.

PS H:\Read\num\x64\Release> .\eSievePro
Eratosthenes sieve: a method to find out all primes below the number that you specify here please: 1234567

Only the sum of all primes needed [y/n](y as default):

Start to work out the sum of all primes below 1234567…
95360 primes found in 0 milliseconds.

PS H:\Read\num\x64\Release>

用C++实现的Eratosthenes筛法程序对比,性能有显著提升。

增强Eratosthenes筛法的C++程序实现

为说明方便,把用C++实现的Eratosthenes筛法程序里的实现称为eSieve,而把这里的新实现称为eSievePro。

宏定义和全局量定义

1 typedef unsigned char u8;
2 typedef unsigned long ulong;
3 static ulong s_last = 0;
4 static u8* s_pOdd = NULL;
5 static std::vector<ulong> s_vecPrime;

对照eSieve的相应部分,这里只是把之前的s_pAll换成了s_pOdd。

main函数实现

 1 int main()
2 {
3 printf(" Eratosthenes sieve: 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 ulong raw_last = strtoul(strInput.c_str(), 0, 10);
7 if (raw_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", raw_last);
16 else
17 printf("\n Start to work out the sum of all primes below %u...\n", raw_last);
18 if (!eSievePro(raw_last, bDetail))
19 return 0;
20 if (bDetail)
21 showDetails();
22 return 0;
23 }

这里的main函数实现保留了交互输入的实现代码,而把增强的Eratosthenes筛法实现放到了单独的eSievePro函数里。

eSievePro函数实现

 1 bool eSievePro(ulong raw_last, bool bDetail)
2 {
3 DWORD tickBegin = GetTickCount();
4 s_last = raw_last / 2;
5 s_pOdd = new u8[s_last];
6 if (!s_pOdd) {
7 printf("Lack of memory.\n");
8 return false;
9 }
10 ulong sum = 1;
11 ulong curPrime = 2;
12 if (bDetail)
13 s_vecPrime.push_back(curPrime);
14 if (raw_last == 3)
15 goto Ending;
16
17 memset(s_pOdd, 1, s_last);
18 ulong curPrimeIdx = 0;
19 while (true) {
20 renewCurrentPrime(curPrimeIdx);
21 ++sum;
22 curPrime = (curPrimeIdx + curPrimeIdx) + 1;
23 if (bDetail)
24 s_vecPrime.push_back(curPrime);
25 s_pOdd[curPrimeIdx - 1] = 0;
26 ulong sqr = curPrime * curPrime;
27 if (sqr > raw_last)
28 break;
29 ulong step = curPrime + curPrime;
30 for (ulong idx = sqr; idx < raw_last; idx += step) {
31 s_pOdd[(idx - 1) / 2 - 1] = 0;
32 }
33 }
34 /// pick up all the left primes
35 for (ulong idx = curPrime + 2; idx < raw_last; idx += 2) {
36 ulong halfIdx = (idx - 1) / 2 - 1;
37 if (s_pOdd[halfIdx] == 1) {
38 ++sum;
39 if (bDetail)
40 s_vecPrime.push_back(idx);
41 }
42 }
43
44 Ending:
45 printf(" %u primes found in %u milliseconds.\n\n", sum, GetTickCount() - tickBegin);
46 delete []s_pOdd;
47 return true;
48 }

 4     s_last = raw_last / 2;
5 s_pOdd = new u8[s_last];

这两行代码可以看出,s_pOdd的动态申请的空间是原来的一半,就是说原来的动态数组s_pAll记录的是1、2、3、……、upperLimit(upperLimit指代交互输入的上界值)所有自然数的素合状态(1表示素数,0表示合数);而这里的动态数组s_pOdd只用一半的空间来记录1到upperLimit之内的所有奇数的素合状态。这样处理不但节省了存储空间,而且直接省去了Eratosthenes筛法中用2筛去所有偶数的处理过程。

如下这几行筛法实现代码

26         ulong sqr = curPrime * curPrime;
29         ulong step = curPrime + curPrime;
30 for (ulong idx = sqr; idx < raw_last; idx += step) {
31 s_pOdd[(idx - 1) / 2 - 1] = 0;
32 }

相比之前的实现代码

36         for (int idx = curPrime - 1; idx <= s_last - 1; idx += curPrime) {
37 s_pAll[idx] = 0;
38 }

有两个改进之处。用一个实例说明一下:比如用Eratosthenes筛法筛去1到1000之内的合数,依次筛去2的倍数、3的倍数、5的倍数、7的倍数,当开始筛去11的倍数时,会依次筛去22、33、44、55、66、77、88、99、110、121、132、143、……,但是22、33、44、55、66、77、88、99、110这些数因为小于11*11,必然有小于11的素因素,说明这些数必然会在此前筛去2、3、5、7等素数的倍数的过程中被筛掉,因此当筛去一个素数的倍数时,从该素数的平方为起点开始筛会减少重复,在上面的代码对比上用标黄部分表示。

另一个改进之处,在上面的代码对比上用标蓝部分表示。仍沿用刚才的实例说明,当从121开始筛去11的倍数时,会依次筛去121、132、143、154、165、176、187、……,从这个列表可以看出奇数偶数交替出现,这也很好解释,因为筛法当前用到的素数(curPrime)是一个奇数,设为2n+1,它的平方也是奇数(即4nn+4n+1),再加上2n+1就会变成偶数(即4nn+6n+2),再加上2n+1又会变成奇数(4nn+8n+3),……。因为这些偶数在最早筛去2的倍数时就已经被筛去了,而且2之后的素数都是奇数,所以可以把2之后的筛法步长调整为当时使用素数的两倍,这样性能可以提升一倍。

当然,上述两个改进之处并不能彻底避免多次重复筛去一个合数的问题。比如,刚才的实例中使用步长22会依次筛去121、143、165、187,但是里面的165=11*5*3,在之前已经被3筛去过一次,又被5筛去过一次。

renewCurrentPrime和showDetails

1 bool renewCurrentPrime(ulong& primeIdx)
2 {
3 while (primeIdx < s_last) {
4 ++primeIdx;
5 if (s_pOdd[primeIdx - 1] == 1)
6 return true;
7 }
8 return false;
9 }

这里的renewCurrentPrime和eSieve实现的renewCurrentPrime几乎一样,只是因为s_last和输入参数的含义有变化以及s_pOdd与此前的的s_pAll的不同含义而有些微不同。

showDetails的实现则完全没有变动,这里不再重复放了。

其他

https://github.com/readalps/eSievePro上放了eSievePro实现的源码文件,以及一个运行结果文件。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,000
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,512
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,358
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,141
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,771
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,849