筛法求素数
来源:互联网 发布:foobar2000音质优化版 编辑:程序博客网 时间:2024/06/05 10:45
genPrime和genPrime2是筛法求素数的两种实现,其实是一个思路,表示方法不同而已。
具体思路在注释中已经含有。
#include<iostream> #include<math.h> #include<stdlib.h>using namespace std; const int MAXV = 100; //素数表范围 bool flag[MAXV+1]; //标志一个数是否为素数 int prime[MAXV+1]; //素数表,下标从0开始 int size=0; //素数个数 void genPrime(int max) { memset(flag, true, sizeof(flag));//首先对标签数组进行初始化,全部设为true。 for(int i = 2; i <= max / 2; i++) { /* 从2开始,删除2的倍数 */ if(flag[i]) { //j=i<<1等价于 j=i*2,即j是i的两倍,而最后的j+=i,则表示下一个循环j是i的3倍,接着4倍。。。 //i的所有2~N倍数肯定都不是素数,因此将flag置为0,直到最后一位。 for(int j = i << 1 ; j <= max; j += i) { flag[j] = false; } } } for(int i = 2 ; i <= max; i++) { if(flag[i]) { prime[size++] = i;//存储素数。将所有标志位依然为1的标志写入素数数组中去。 } } } void genPrime2(int max) { memset(flag, true, sizeof(flag));//首先对标签数组进行初始化,全部设为true。 int sq=sqrt((double)max)+1; //一个数 n 如果是合数,那么它的所有的因子不超过sqrt(n) int i,j, k; for(i = 2;i<=sq; i++) { if(flag[i]) for(j=2,k=max/i+1;j<k;j++) flag[i*j] = false; //所有i的j倍都不是素数 } for( i = 2 ; i <= max; i++) { if(flag[i]) { prime[size++] = i;//存储素数。将所有标志位依然为1的标志写入素数数组中去。 } } } int main() { // genPrime(MAXV); genPrime2(MAXV); //输出所有素数。 for(int i=0;i<size;i++) cout<<prime[i]<<" "; cout<<endl; system("pause"); return 0; } /*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:补充于2011-6-14
参考博客:
1.http://www.cppblog.com/shyli/archive/2007/05/18/24334.html
2.http://blog.sina.com.cn/s/blog_4b5210840100cm4r.html
3.http://blog.hjenglish.com/bedford/archive/2008/10/11/401082.html
一般筛法求素数+快速线性筛法求素数
筛法其实就是以空间换时间的一个最好的证明。空间复杂度增加了,时间复杂度降低了。这个斐波那契数列求解类似,如果用递归,那么时间复杂度很慢,如果将用数组存储一个斐波那契数列,则时间复杂度降低到线性。
上面的参考博客2有一些解释,3则给出了具体的改进算法。有空再研究。
素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功。
基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2 。。N^(0.5) ,看看能否整除N。
如果需要判断的次数较多,则先用下面介绍的办法预处理。
一般的线性筛法
首先先介绍一般的线性筛法求素数
这种方法比较好理解,初始时,假设全部都是素数,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数(注意上面的 i*i , 比 i*2 要快点 ),把这些合数都筛掉,即算法名字的由来。
但仔细分析能发现,这种方法会造成重复筛除合数,影响效率。比如10,在i=2的时候,k=2*15筛了一次;在i=5,k=5*6 的时候又筛了一次。所以,也就有了快速线性筛法。
快速线性筛法
快速线性筛法没有冗余,不会重复筛除一个数,所以“几乎”是线性的,虽然从代码上分析,时间复杂度并不是O(n)。先上代码
首先,先明确一个条件,任何合数都能表示成一系列素数的积。
不管 i 是否是素数,都会执行到“关键处1”,
①如果 i 都是是素数的话,那简单,一个大的素数 i 乘以不大于 i 的素数,这样筛除的数跟之前的是不会重复的。筛出的数都是 N=p1*p2的形式, p1,p2之间不相等
②如果 i 是合数,此时 i 可以表示成递增素数相乘 i=p1*p2*...*pn, pi都是素数(2<=i<=n), pi<=pj ( i<=j )
p1是最小的系数。
根据“关键处2”的定义,当p1==prime[j] 的时候,筛除就终止了,也就是说,只能筛出不大于p1的质数*i。
我们可以直观地举个例子。i=2*3*5
此时能筛除 2*i ,不能筛除 3*i
如果能筛除3*i 的话,当 i' 等于 i'=3*3*5 时,筛除2*i' 就和前面重复了。
需要证明的东西:
- 一个数会不会被重复筛除。
- 合数肯定会被干掉。
根据上面红字的条件,现在分析一个数会不会被重复筛除。
设这个数为 x=p1*p2*...*pn, pi都是素数(1<=i<=n) , pi<=pj ( i<=j )
当 i = 2 时,就是上面①的情况,
当 i >2 时, 就是上面②的情况, 对于 i ,第一个能满足筛除 x 的数 y 必然为 y=p2*p3...*pn(p2可以与p1相等或不等),而且满足条件的 y 有且只有一个。所以不会重复删除。
证明合数肯定会被干掉? 用归纳法吧。
类比一个模型,比如说我们要找出 n 中2个不同的数的所有组合 { i , j } ,1<=i<=n, 1<=j<=n,
我们会这么写
for (i=1; i<n; ++i )
for (j=i+1; j<=n; ++j)
{
/////
}
我们取 j=i+1 便能保证组合不会重复。快速筛法大概也是这个道理,不过这里比较难理解,没那么直观。
1楼提供的方法,我整理下
//偶数显然不行,所以先去掉偶数。可以看作上面第一种的优化吧。
//不过这种方法不太直观,不太好理解。
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- 筛法求素数
- jstack对线程信息的分析
- A - Theatre Square
- 使用MERGE命令同步数据
- 我所理解的Cocos2d-x Cocos2d-x 内存管理机制
- 简述Java命令行参数、JVM、打包Java程序、JAR文件
- 筛法求素数
- 【理解JVM】 深入分析Java ClassLoader原理
- cannot open shared object file: No such file or director解决方案
- nodejs--fs模块
- Jquery 事件冒泡的介绍以及如何阻止事件冒泡
- Fat Models
- 文件下载不能使用ajax的替代解决方案
- HDU5443->ST表
- [libevent]event_base_loop()