数论第一篇随笔,就讲一下质数筛法吧
质数,也称做素数,在数学中有着重要的地位,有很多问题与算法都与质数有关(比如给你的hash函数选择一个好的质数可以让你的RP++)。对质数的最基本操作,就是输出所有不大于n(n∈N*)的素数。
最简单的方法:首先逐个枚举从 2 ~ n 的数a,分别模 2 ~ sqrt(a)(即a的平方,想一下为什么只需要判到sqrt(a)),若余数为0,那么a不是质数。
实现代码:
1 #include<stdio.h> 2 #include<math.h> 3 int n; 4 int main(){ 5 scanf("%d",&n); 6 if(n<2){ 7 printf("0"); 8 return 0; 9 } 10 for(int i=2;i<=n;i++){ 11 bool f=true; 12 for(int j=2;j<=sqrt(i);j++) 13 if(i%j==0){ 14 f=false; 15 break; 16 } 17 if(f) printf("%d ",i); 18 } 19 return 0; 20 }
以上朴素算法复杂度为O(nsqrt(n))
既然叫做朴素算法,那肯定有效率比它高的算法,那就是筛法求质数
它是通过划掉n内每个已求出质数的所有倍数来实现,留下来的数就都是不大于n的质数
举个例子,我们要求不大于15的所有质数: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1不是质数,2是最小的质数,从2开始划掉2的倍数: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
轮到3,3没有被划掉,是质数,所以划掉3的倍数: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
然后依次是5、7、11、13,重复上面的步骤。
其实这种情况下在划掉所有3的倍数时就已经求出了所有不大于15的质数,即没有被划掉的数
实现代码:
1 #include<stdio.h> 2 #include<string.h> 3 #define maxn 2333333 4 int n; 5 bool zs[maxn]; 6 int main(){ 7 memset(zs,0,sizeof(zs)); 8 scanf("%d",&n); 9 zs[0]=zs[1]=1; 10 for(int i=2;i*i<=n;i++)//i<=sqrt(n) ===> i*i<=n 11 if(!zs[i]) 12 for(int j=i+i;j<=n;j+=i) 13 zs[j]=1; 14 for(int i=2;i<=n;i++) 15 if(!zs[i]) 16 printf("%d ",i); 17 return 0; 18 }
这种做法的算法复杂度为O(nloglog(n))
那有没有比以上效率更高的做法呢,有的!
如果按以上方法做,会有计算冗余,比如60这个数,会被2,3,5各筛一次
所有就有了下面复杂度为O(n)的方法
实现代码:
1 #include<stdio.h> 2 #include<string.h> 3 #define maxn 2333333 4 bool zs[maxn]; 5 int n,prime[maxn],num; 6 int main(){ 7 memset(zs,0,sizeof(zs)); 8 scanf("%d",&n); 9 zs[0]=zs[1]=1;num=0; 10 for(int i=2;i<=n;i++){ 11 if(!zs[i]){ 12 prime[++num]=i; 13 printf("%d ",prime[num]); 14 } 15 for(int j=1;j<=num&&i*prime[j]<=n;j++){ 16 zs[i*prime[j]]=1; 17 if(!(i%prime[j])) break; 18 } 19 } 20 return 0; 21 }
这种方法就叫质数的线性筛法
详细的以后再讲吧