素数算法详解
2015-08-04 11:49
267 查看
素数,简而言之,除1和其本身外没有其他约数。求素数是我们经常在编程与刷oj题经常遇到的,其出现频率仅此于排序。所以有必要对筛素数的各种算法进行深刻理解,对算法进行优化也是必不可少的过程。(写一个高效的代码以后当模板用,嘿嘿~)。
如果要把1000000内的素数打表,只需要用下面最简单的找素数的算法:
算法一:我们知道如果要判断n是否为素数,只要用n对2到根号n的数进行取余运算即可,如果都不能整除n,则n为素数,不然,n不是素数。这样只需对2到1000000内的数进行一一判断即可。此算法的时间复杂度为O(n*根号n)。
源代码:
运行结果:
当数据改为10000000时:
可见此算法在时间复杂度上有缺陷。
算法二:厄尔多塞筛法
1.选取2,将2的倍数筛掉
2.选其后未被删掉的数筛掉其倍数,重复此步骤直到所选的数大于根号n
3.未被筛掉的数即为素数
时间复杂度为O(n*loglogn)。
源代码:
运行结果:
当数据改为100000000时:
我们还有更高的要求–>线性的时间复杂度!
算法三:线性筛
通过剪枝防止一个数被多次筛掉,从而节省时间。因为每个数只被筛掉一次,所以时间复杂度为O(n)。
源代码:
当数据大小为100000000时 结果为:
此算法的时间复杂度依然可以优化,因为我们知道当n>3
时只有6*n-1与6*n+1可能为素数。(一般刷题,用算法三就可以了,线性的时间复杂度已经可以满足要求,往下探究只是想知道能否把100000000的数据规模在1秒内求出来)。
优化源代码如下:
运行结果:
上图的运行时间由于运行机器原因只能作为对比值。在同学电脑上运行线筛+6*n+1(-1)的代码,运行时间<1s。
线性筛+6*n+1(6*n-1)时间复复杂度已经足够低了,再优化嘛,反正我是没有方法了,而且过度的优化会导致代码的雍长,没有实际价值。
如果要把1000000内的素数打表,只需要用下面最简单的找素数的算法:
算法一:我们知道如果要判断n是否为素数,只要用n对2到根号n的数进行取余运算即可,如果都不能整除n,则n为素数,不然,n不是素数。这样只需对2到1000000内的数进行一一判断即可。此算法的时间复杂度为O(n*根号n)。
源代码:
[code]#include<iostream>//O(n*sqrt(n))) #include<cstdio> #include<ctime> #define M 1000000 using namespace std; int prime[M]; void Prime(int n){ int k=0; for(int i=2;i<n;i++){ int j; for(j=2;j*j<=i;j++) if(i%j==0) break; if(j*j>i) prime[k++]=i; } } int main(){ int time_1=clock(); Prime(M); int time_2=clock(); cout<<"Time is "<<(double)(time_2-time_1)/CLOCKS_PER_SEC<<endl; for(int i=0;i<100;i++) cout<<prime[i]<<" "; cout<<endl; return 0; }
运行结果:
当数据改为10000000时:
可见此算法在时间复杂度上有缺陷。
算法二:厄尔多塞筛法
1.选取2,将2的倍数筛掉
2.选其后未被删掉的数筛掉其倍数,重复此步骤直到所选的数大于根号n
3.未被筛掉的数即为素数
时间复杂度为O(n*loglogn)。
源代码:
[code]#include<iostream>//未优化的厄尔多塞筛法O(n*loglogn) #include<cstring> #include<ctime> #define M 10000000 #define N 10000000 using namespace std; bool tag[M]; int prime ; void Prime(int n){ for(int i=3;i<n;i++) if(i%2==0) tag[i]=false; for(int i=3;i*i<n;i+=2){ for(int j=i*i;j<n;j+=i) tag[j]=false; } int k=0; for(int i=2;i<n;i++) if(tag[i]) prime[k++]=i; } int main(){ memset(tag,true,sizeof(tag)); int time_1=clock(); Prime(M); int time_2=clock(); cout<<(double)(time_2-time_1)/CLOCKS_PER_SEC<<endl; for(int i=0;i<100;i++) cout<<prime[i]<<" "; cout<<endl; return 0; }
运行结果:
当数据改为100000000时:
我们还有更高的要求–>线性的时间复杂度!
算法三:线性筛
通过剪枝防止一个数被多次筛掉,从而节省时间。因为每个数只被筛掉一次,所以时间复杂度为O(n)。
源代码:
[code]#include<iostream>//优化后的素筛 O(n) #include<cstring> #include<ctime> #define M 100000000 #define N 10000000 using namespace std; int prime ; bool tag[M]; void Prime(int n){ int k=0; for(int i=2;i<n;i++){ if(tag[i]){ prime[k++]=i; } for(int j=0;j<k&&i*prime[j]<n;j++){ tag[prime[j]*i]=false; if(i%prime[j]==0) break; } } } int main(){ memset(tag,true,sizeof(tag)); int time_1=clock(); Prime(M); int time_2=clock(); cout<<"Time is "<<(double)(time_2-time_1)/CLOCKS_PER_SEC<<endl; for(int i=0;i<100;i++) cout<<prime[i]<<" "; cout<<endl; return 0;: }
当数据大小为100000000时 结果为:
此算法的时间复杂度依然可以优化,因为我们知道当n>3
时只有6*n-1与6*n+1可能为素数。(一般刷题,用算法三就可以了,线性的时间复杂度已经可以满足要求,往下探究只是想知道能否把100000000的数据规模在1秒内求出来)。
优化源代码如下:
[code]#include<iostream> #include<cstring> #include<ctime> #define M 100000000 #define N 10000000 using namespace std; int prime ={2,3}; bool tag[M]; void Prime(int n){ for(int i=1;i*6-1<n;i++){ tag[i*6-1]=true; tag[i*6+1]=true; } int k=2; for(int i=1;i*6-1<n;i++){ int t=i*6-1; int f=i*6+1; if(tag[t]) prime[k++]=t; if(tag[f]) prime[k++]=f; int flag_1=1,flag_2=1; for(int j=2;j<k&&(prime[j]*t<n||prime[j]*f<n);j++){ if(prime[j]*t<n&&flag_1) tag[prime[j]*t]=false; if(prime[j]*f<n&&flag_2) tag[prime[j]*f]=false; if(t%prime[j]==0) flag_1=0; if(f%prime[j]==0) flag_2=0; if(!flag_1&&!flag_2) break; } } cout<<k<<endl; } int main(){ memset(tag,false,sizeof(tag)); int time_1=clock(); Prime(M); int time_2=clock(); cout<<"Time is "<<(double)(time_2-time_1)/CLOCKS_PER_SEC<<endl; for(int i=0;i<100;i++) cout<<prime[i]<<" "; cout<<endl; return 0; }
运行结果:
上图的运行时间由于运行机器原因只能作为对比值。在同学电脑上运行线筛+6*n+1(-1)的代码,运行时间<1s。
线性筛+6*n+1(6*n-1)时间复复杂度已经足够低了,再优化嘛,反正我是没有方法了,而且过度的优化会导致代码的雍长,没有实际价值。
相关文章推荐
- poj 1837 dp
- Exploded location overlaps an existing deployment解决办法
- c# 遍历控件
- 使用mybatis, 如何获取刚插入数据的id
- Android MediaPlayer与Http Proxy结合之基础篇
- OpenCV Cut Image via ROI 根据兴趣区域剪裁图片
- 江湖恩仇录之PHP程序CPU高占用优化经历分享
- java操作properties文件
- MYSQL问题解决方案:Access denied for user 'root'@'localhost' (using password:YES)
- win7下debug native 环境搭建
- Android通过Xutils注解实例化以及事件绑定
- linux kafka 搭建运行环境
- wireshark抓包图解 TCP三次握手/四次挥手详解
- Squares
- HDU 5078--Osu!【水题】
- C++内联函数
- Squares 分类: POJ 2015-08-04 11:46 3人阅读 评论(0) 收藏
- 2.0-iptables详解
- centos6.6_x64升级firefox
- Squid Epoll网络模型