埃式筛法(素数筛) + 区间素数筛 + 应用
来源:互联网 发布:cpu温度测试软件 编辑:程序博客网 时间:2024/06/07 06:49
埃拉托斯特尼筛法,简称埃氏筛或爱氏筛
埃式筛法:给定一个正整数n(n<=10^6),问n以内有多少个素数?
做法:做法其实很简单,首先将2到n范围内的整数写下来,其中2是最小的素数。将表中所有的2的倍数划去,表中剩下的最小的数字就是3,他不能被更小的数整除,所以3是素数。再将表中所有的3的倍数划去……以此类推,如果表中剩余的最小的数是m,那么m就是素数。然后将表中所有m的倍数划去,像这样反复操作,就能依次枚举n以内的素数,这样的时间复杂度是O(nloglogn)
例如 : 一些题目中求1-n的素数有多少个, 如果直接筛, 复杂度为O(n*√n), 当n达到1e6是必定会T, 所以就要用到埃式筛法.
代码:
bool ispri[maxn]; //是不是素数.int prime[maxn]; //第几个素数.int getpri(int n) //返回1-n的素数个数.{ Fill(ispri,true); ispri[0] = ispri[1] = false; int p = 0; for(int i=2;i<=n;i++){ if(ispri[i]){ prime[p++] = i; for(int j=i*2;j<=n;j+=i) ispri[j] = false; } } return p;}
简单应用 :HDU — 1262
思路: 先对范围内的素数进行打表,然后因为是要选出相邻最近的两个数,所以需要从中间开始找.
(这道题很水, 直接找也能过, 复杂度应该合适)
代码如下:
/** @Cain*/const int maxn=1e6+5;bool ispri[maxn]; //是不是素数.int prime[maxn]; //第几个素数.int getpri(int n) //返回1-n的素数个数.{ Fill(ispri,true); ispri[0] = ispri[1] = false; int p = 0; for(int i=2;i<=n;i++){ if(ispri[i]){ prime[p++] = i; for(int j=i*2;j<=n;j+=i) ispri[j] = false; } } return p;}void solve(){ getpri(maxn); int i,n; while(~scanf("%d",&n)){ for(i=n/2;i>=0;i--){ //从中间开始找,如果能找到,则肯定是相邻最近的两个素数,所以输出. if(ispri[i] && ispri[n-i]) break; } printf("%d %d\n",i,n-i); }}
高级应用 : 区间素数筛:给定两个正整数l, r( 1<= L < R <= 10^12, R - L <=10^6),请问[L, R]内有多少个素数?
主要思想:对于数R 以内的合数的最小质因数不会超过√R(反正我是当结论来背的). 所以先求出1-1e6以内的素数(因为1e6的平方刚好是1e12就题目的范围), 再用这些素数去筛出l - r之间的合数,剩下的就是L- R之间的素数了. 二次筛法 …
区间长度只有1e6,所以在存的时候虽然不能直接存每个数,但是可以加个偏移量 L (仔细想想),这样便可存的下 .
注意1既不是素数也不是合数.
板子:
/** @Cain*/const int maxn = 1e6+5;bool pri[maxn];bool ispri[maxn];void getpri(){ Fill(pri,true); pri[0] = pri[1] = false; for(int i=2;i<=maxn;i++){ if(pri[i]){ for(int j=2*i;j<=maxn;j+=i) pri[j]=false; } }}void seg_getpri(ll L,ll R){ Fill(ispri,true); if(1ll - L >= 0) ispri[1-L] = false; for(ll i=2;i*i <= R;i++){ if(pri[i]){ for(ll j = max( (L+i-1) / i, 2LL)*i; j<=R ; j+=i) ispri[ j-L ] = false; } }}void solve(){ ll L,R; scanf("%lld%lld",&L,&R); getpri(); seg_getpri(L,R); int cnt=0; for(int i=0;i<=R-L;i++){ if(ispri[i]) cnt++; } printf("%d\n",cnt);}
代码解释版:
/** @Cain*/const int maxn = 1e6+5;bool pri[maxn]; //保存1-1e6的素数.bool ispri[maxn]; //ispri[i-L]=true代表i是素数 //加了个偏移量L.void getpri() //预处理素数{ Fill(pri,true); pri[0] = pri[1] = false; for(int i=2;i<=maxn;i++){ if(pri[i]){ for(int j=2*i;j<=maxn;j+=i) pri[j]=false; } }}void seg_getpri(ll L,ll R) //[L,R]区间筛{ Fill(ispri,true); if(1ll - L >= 0) ispri[1-L] = false; //易错因为1不是素数也不是合数,这也是区间筛的一个易错bug for(ll i=2;i*i <= R;i++){ if(pri[i]){ //也是坑点, 因为L可能比i小, 但是不能从1开始取. 必须从2开始. //素数的倍数落在L-R区间的要筛掉. //(L+i-1)/i 得到最接近 L 的 i 的倍数, 最低是i的2倍, 然后筛选(这样j就都在L,R里内了) for(ll j = max( (L+i-1) / i, 2LL)*i; j<=R ; j+=i) ispri[ j-L ] = false; } }}void solve(){ ll L,R; scanf("%lld%lld",&L,&R); getpri(); seg_getpri(L,R); int cnt=0; for(int i=0;i<=R-L;i++){ if(ispri[i]) cnt++; } printf("区间[%lld,%lld]里的素数个数: %d\n",L,R,cnt);}
例题 poj – 2689
//题意:输入区间[L,U],其中L和U为 int 范围的整数,区间最大为1000000. 求出[L,U]中,相邻素数之差最大和最小的素数对. 当存在多个时,输出较小的素数对
//思路: 就是区间筛, 这个范围还小一点, 数只用枚举到5e5. 然后在区间(还是要1e6长)素数中跑一遍数一下就可以了.
AC Code
/** @Cain*/const int maxn = 1e6+5;bool pri[maxn];bool ispri[maxn];void getpri(){ Fill(pri,true); pri[0] = pri[1] = false; for(int i=2;i<=maxn/2;i++){ if(pri[i]){ for(int j=2*i;j<=maxn/2;j+=i) pri[j]=false; } }}void seg_getpri(ll L,ll R){ Fill(ispri,true); if(1ll - L >= 0) ispri[1-L] = false; for(ll i=2;i*i <= R;i++){ if(pri[i]){ for(ll j = max( (L+i-1) / i, 2LL)*i; j<=R ; j+=i) ispri[ j-L ] = false; } }}void solve(){ ll L,R; while(~scanf("%lld%lld",&L,&R)){ getpri(); seg_getpri(L,R); int maxx = -inf, minn = inf; int pos = -1,x1,y1,x2,y2; for(int i=0;i<=R-L;i++){ if(ispri[i]){ if(pos == -1){ pos = i; continue; } if(maxx < i - pos){ maxx = i - pos; x1 = pos+L; //把偏移量加上就行了. y1 = i+L; } if(minn > i - pos){ minn = i - pos; x2 = pos+L; y2 = i+L; } pos = i; } } if(maxx == -inf) printf("There are no adjacent primes.\n"); else printf("%d,%d are closest, %d,%d are most distant.\n",x2,y2,x1,y1); }}
- 埃式筛法(素数筛) + 区间素数筛 + 应用
- 素数表,区间素数筛
- 区间素数筛模板
- 区间筛素数
- 区间素数筛
- loj #6235. 区间素数个数(素数筛)
- HDU6069 Counting Divisors(区间素数筛)
- 大数区间筛素数 线性
- 洛谷p1865区间筛素数
- POJ 2689 Prime Distance(区间相邻素数最大、小距离,区间素数筛)
- poj 2689解题报告(区间筛素数,经典)
- LightOJ 1197 Help Hanzo(区间素数筛)
- 51NOD 1434 区间LCM(素数筛)
- poj 2689 Prime Distance(大数区间素数筛法)
- LightOJ 1197(大数范围筛区间内素数个数)
- 素数统计 平移区间筛质数(1e9)
- POJ 2689 Prime Distance(区间素数筛)
- [模板][洛谷P1835]素数密度(区间筛)
- 递归的函数
- B/S架构中,缓存处理的一点点感想。
- iOS HTTP原理-请求&响应~笔记
- 媒体查询分界点阈值
- 仿网易新闻app下拉标签选择菜单
- 埃式筛法(素数筛) + 区间素数筛 + 应用
- JS实现div块的拖放,调换位置
- css样式适配IE11
- 从上往下打印二叉树
- 万圣节派对
- Math类常见用法
- WPF样式使用方法
- C++ 跨平台编程宏定义
- Centos7配置固定IP