运行示例

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实现的源码文件,以及一个运行结果文件。

用C++实现的增强Eratosthenes筛法程序的更多相关文章

  1. 用C++实现的增强Euler筛法程序

    运行示例 PS H:\Read\num\x64\Release> .\eulerSievePro EulerSievePro: a method to find out all primes b ...

  2. 用C++实现的Eratosthenes筛法程序

    运行示例 只输出素数总数的运行示例 PS H:\Read\num\x64\Release> .\esieve.exe Eratosthenes sieve: a method to find o ...

  3. 用C++实现的Euler筛法程序

    Euler筛法介绍 以筛出100以内(含100)的所有素数为例来说明一下欧拉筛法的原理. 和Eratosthenes筛法一样,Euler筛法也从2开始筛,但Eratosthenes筛法会把2的倍数一批 ...

  4. C语言程序设计100例之(12):Eratosthenes筛法求质数

    例12   Eratosthenes筛法求质数 问题描述 Eratosthenes筛法的基本思想是:把某范围内的自然数从小到大依次排列好.宣布1不是质数,把它去掉:然后从余下的数中取出最小的数,宣布它 ...

  5. 由Eratosthenes筛法演变出的一种素数新筛法

    这两天和walls老师交流讨论了一个中学竞赛题,我把原题稍作增强和变形,得到如下一个题: 从105到204这100个数中至少要选取多少个数才能保证选出的数中必有两个不是互素的? 我们知道最小的几个素数 ...

  6. 25个增强iOS应用程序性能的提示和技巧(高级篇)(2)

    25个增强iOS应用程序性能的提示和技巧(高级篇)(2) 2013-04-16 14:56 破船之家 beyondvincent 字号:T | T 在开发iOS应用程序时,让程序具有良好的性能是非常关 ...

  7. 25个增强iOS应用程序性能的提示和技巧(高级篇)(1)

    25个增强iOS应用程序性能的提示和技巧(高级篇)(1) 2013-04-16 14:56 破船之家 beyondvincent 字号:T | T 在开发iOS应用程序时,让程序具有良好的性能是非常关 ...

  8. 25个增强iOS应用程序性能的提示和技巧(中级篇)(3)

    25个增强iOS应用程序性能的提示和技巧(中级篇)(3) 2013-04-16 14:42 破船之家 beyondvincent 字号:T | T 本文收集了25个关于可以提升程序性能的提示和技巧,分 ...

  9. 25个增强iOS应用程序性能的提示和技巧(中级篇)(2)

    25个增强iOS应用程序性能的提示和技巧(中级篇)(2) 2013-04-16 14:42 破船之家 beyondvincent 字号:T | T 本文收集了25个关于可以提升程序性能的提示和技巧,分 ...

随机推荐

  1. Spring事务管理中的配置文件(三)

    在开发中,遇到了sql语句报错,但是并没有回滚的情况. 经过几天的排查,终于找到了事务没有回滚的原因. 原来的项目用的是informix的数据库,原来针对事务回滚的机制都是好用的.我本地用的是mysq ...

  2. Oracle导入dmp文件:ORACLE错误12899而拒绝行的问题如何解决

    原文链接:https://www.2cto.com/database/201804/736027.html

  3. Android 开发必备的知识点——JVM基础【转】

    image 1.JVM与操作系统的关系 Java Virtual Machine JVM 全称 Java Virtual Machine,也就是我们耳熟能详的 Java 虚拟机.它能识别 .class ...

  4. 大数据学习(18)—— Flume介绍

    老规矩,学习新东西先上官网瞅瞅Apache Flume Flume是什么 Flume是一个分布式.可靠的大规模高效日志收集.汇聚和传输的这么一个服务.它的架构基于流式数据,配置简单灵活.它具备可调节的 ...

  5. 【LeetCode】209. 长度最小的子数组

    209. 长度最小的子数组 知识点:数组:前缀和:二分法:双指针:滑动窗口 题目描述 给定一个含有 n 个正整数的数组和一个正整数 target . 找出该数组中满足其和 ≥ target 的长度最小 ...

  6. Session与Cookie的原理以及使用小案例>从零开始学JAVA系列

    目录 Session与Cookie的原理以及使用小案例 Cookie和Session所解决的问题 Session与Cookie的原理 Cookie的原理 Cookie的失效时机 小提示 Session ...

  7. vue中rem的转换

    1 function rems(doc: any, win: any): void { 2 let docEl = doc.documentElement, 3 resizeEvt = 'orient ...

  8. 全网最硬核Handler面试题深度解析

    1.简述Handler的实现原理 Android 应用是通过消息驱动运行的,在 Android 中一切皆消息,包括触摸事件,视图的绘制.显示和刷新等等都是消息.Handler 是消息机制的上层接口,平 ...

  9. 从事 Android应用开发4年有余,现在工资7500。很不爽!怎么办?

    最近到某论坛看到一个帖子: 坐标北京,在一个公司从事android应用开发4年有余(毕业至今没换过公司).公司利润越来越大,工资却每年长1000,如今才到7500.琢磨着换工作,又不想扔下这四年来逐步 ...

  10. 线程的分离状态(detached state)

    说到线程的分离状态,我认为,之所以会有这个状态,是因为系统对某些线程的终止状态根本不感兴趣导致的. 我们知道,进程中的线程可以调用: [cpp] view plaincopyprint? int pt ...