这篇文章介绍了素数处理的常用方法-艾式筛法及其位优化版本.
目录
1.hdoj 1397 Goldbach’s Conjecture
1.埃式筛法是什么?
首先埃式筛法是一种算法,核心是利用打表法高效获取0到n内的素数,模版型算法.
2.埃式筛法有什么用?
一般用于解决这种问题:
素数相关的问题.
标志性经典问题:
现给你一个正整数n(n<1000000),问0到n内的素数个数为多少?
想这种素数问题数据小时可以用n*n解,可算法嘛,就是用来在大数据下体现高效的,这里就需要用到埃式筛法.
3.埃式筛法实现思路
处理素数之前我们先了解一下什么是素数:
质数(prime number)又称素数,表示一个大于1的自然数,除了1和它本身外,不能被其他自然数整除.
故而我们知道了,如果一个数能被除了1和它本身的数整除,那它肯定不是素数,故而很早以前有人研究出了埃式筛法,先定义一个prime数组用于存放找到的素数,标志数组falg标志每个数是不是素数,最开始除了0和1意外0到n每个数都是素数,我们知道0,1不是素数,2是素数.故而我们从2开始,
先把2存进prime数组里,然后将后面小于等于n又是2的倍数的数字全部标记为不是素数,
然后向后找到第一个未被标记不是素数的数字3,
将3放进prime数组,然后将后面小于等于n又是3的倍数的数字全部标记为不是素数,
然后向后找到第一个未被标记不是素数的数字5,
…….
最后到n时,筛选完毕,0到n内是素数的数都在prime数组中存好了.标志每个数是不是数组的flag数组也做好了,i是素数则flag[i]为true,这就是埃式筛法的思路,如同其名,筛法.
4.模版代码
先是普通的打表
//埃氏筛法 普通打表
#include <stdio.h>
const int maxn=100000;
int prime[maxn],primenum;
bool flag[maxn];
void common_prime(int t){ //prime数组和flag数组都做好
primenum=0;
flag[0]=flag[1]=false;
for(int k=2;k<=t;k++) flag[k]=true;
for(int i=2;i<=t;i++){
if(flag[i]){
for(int j=i<<1;j<=t;j+=i) flag[j]=false;
prime[primenum++]=i;
}
}
}
void common_prime_onlyflag(int t){ //只做flag数组
flag[0]=flag[1]=false;
for(int k=2;k<=t;k++) flag[k]=true;
for(int i=2;i<=t;i++) if(flag[i]) for(int j=i<<1;j<=t;j+=i) flag[j]=false;
}
int main(){
int t=100;
common_prime(t);
for(int i=0;i<primenum;i++) printf("%d ",prime[i]);
printf("\n");
for(int k=0;k<=t;k++) if(flag[k]) printf("%d ",k);
return 0;
}
然后我们介绍一种更节约空间的方法:位压缩法
bool型占1个字节8个位,其实我们只需要一个位就能表示真与假,我们可以用一个int,用他的32个位分别表示32个数是否是素数,这样我们相比与一个bool表示一个数的真假状态能缩小8倍空间!
源代码如下,最关键的是将位置i映射到对应的第i/32个数字的第i%32个二进制位,其它的与普通埃式筛法完全相同
// 位操作求素数
#include <stdio.h>
const int maxn=1000000;
int prime[maxn],primenum;
int flag[maxn/32+1];//数组大小实际缩小8倍
void wei_prime(int t){
primenum=0;
flag[0]|=3;
for(int k=1;k<=t/32+1;k++) flag[k]=0;
for(int i=2;i<=t;i++){
if(!((flag[i/32]>>(i%32))&1)){
for(int j=i*2;j<=t;j+=i) flag[j/32]|=(1<<(j%32));
prime[primenum++]=i;
}
}
}
void wei_prime_onlyflag(int t){
flag[0]|=3;
for(int k=1;k<=t/32+1;k++) flag[k]=0;
for(int i=2;i<=t;i++) if(!((flag[i/32]>>(i%32))&1)) for(int j=i*2;j<=t;j+=i) flag[j/32]|=(1<<(j%32));
}
void printby_prime(){
for(int i=0;i<primenum;i++) printf("%d ",prime[i]);
}
void printby_flag(int t){
for(int i=0;i<=t;i++) if(!((flag[i/32]>>(i%32))&1)) printf("%d ",i);
}
int main(){
int t=100;
wei_prime(t);
printby_prime();
printf("\n");
printby_flag(t);
return 0;
}
5.例题训练
1.hdoj 1397 Goldbach’s Conjecture
Problem Description
Goldbach’s Conjecture: For any even number n greater than or equal to 4, there exists at least one pair of prime numbers p1 and p2 such that n = p1 + p2.
This conjecture has not been proved nor refused yet. No one is sure whether this conjecture actually holds. However, one can find such a pair of prime numbers, if any, for a given even number. The problem here is to write a program that reports the number of all the pairs of prime numbers satisfying the condition in the conjecture for a given even number.
A sequence of even numbers is given as input. Corresponding to each number, the program should output the number of pairs mentioned above. Notice that we are interested in the number of essentially different pairs and therefore you should not count (p1, p2) and (p2, p1) separately as two different pairs.
Input
An integer is given in each input line. You may assume that each integer is even, and is greater than or equal to 4 and less than 2^15. The end of the input is indicated by a number 0.
Output
Each output line should contain an integer number. No other characters should appear in the output.
Sample Input
610120
Sample Output
121
题意分析:
其实就是要你求两素数之和为n的组合数,因为n最大会达到2的15次方即32768,n*n的暴力求解刚好爆掉,故而需要使用埃式筛法,先预处理出0到32768的所有素数是否为素数的flag数组,在对每个输入的n做一次0到n/2的循环判断即可.
ac代码:
#include <stdio.h>
const int maxn=100000;
bool flag[maxn];
void common_prime_onlyflag(int t){
flag[0]=flag[1]=false;
for(int k=2;k<=t;k++) flag[k]=true;
for(int i=2;i<=t;i++) if(flag[i]) for(int j=i<<1;j<=t;j+=i) flag[j]=false;
}
int main(){
int t=1;
for(int i=0;i<15;i++) t*=2;
common_prime_onlyflag(t);
int num;
while(1){
scanf("%d",&num);
if(num==0) break;
int ans=0;
for(int i=2;i<=num/2;i++) if(flag[i]&&flag[num-i]) ans++;
printf("%d\n",ans);
}
return 0;
}
2.待添加高难度题