首页 > 其他 > 详细

素数筛

时间:2020-02-18 23:24:33      阅读:63      评论:0      收藏:0      [点我收藏+]

1.埃氏筛

时间复杂度O(nloglogn)

  传统的寻找素数的方法时间复杂度很高,当数据规模很大时不再实用,对于OJ通常几百ms或1s的限时,我们通常会采用用空间换时间的方法。

  埃氏筛的思路是在一开始创建一个长度为N+1(N为要查找的最大范围)的数组,数组的下标(从1开始)对应要查找的数字,一开始将数组中的元素通过循环全部赋值为1。另外创建一个数组存放已经找到的素数。

  最小的素数为2,是2的倍数的数都不是素数,因此把这些2的倍数的合数标记为0。4 6 8 10 ……都被标记为0。

  接下来,寻找2之后第一个没有被标记的数(这个数一定是素数),这个数是3,把3放进素数数组

  是3的倍数的数都不是素数,因此 6 9 12 15……都被标记为0。

  接下来,寻找3之后第一个没有被标记的数(同样的,这个数也一定为素数),这个数是5,把5放进素数数组

  是5的倍数的数…………

代码实现:

 

 1 #include<stdio.h>
 2 #define N 10000  //N为最大范围,自定。
 3 int is_prime[N+1];
 4 int primes[N];//素数数组大小自己估计给出
 5 
 6 int main(void){
 7     int count=0;  //用于记录已经找到的素数的个数
 8 
 9     for(int i=2;i<=N;i++)
10         is_prime[i]=1;
11     
12     for(int i=2;i<=N;i++)
13         if(is_prime[i]==1){
14             primes[++count]=i;  
15 
16             //这里我们不用循环和余数来判断是否是某数的倍数
17             for(int j=2*i;j<=N;j+=i)
18                 is_prime[j]=0;
19         }
20       
21     for(int i=1;i<=count;i++)
22       printf("%d ",primes[i]);
23         
24      return 0;
25 }

 

2.欧拉筛(线性筛):

时间复杂度:O(n)

  虽然经过优化,埃氏筛(太弱鸡)相比普通方法要快了许多(多???),但当数据规模再大时,依旧TLE,TLE,TLE(重要的事情说三遍)。

  通过分析埃氏筛的过程,我们发现6 12被2筛掉之后又被3筛,我们在做重复的工作。

  欧拉筛的思路是:某合数只被最大非自身质因数筛掉。

 

   跟踪一下实现过程:

   依旧初始化大小为N+1的判定数组(均为1)和一个素数数组。

   最小素数为2。从2开始。

   2:2未被筛去,把2放进素数数组;遍历素数数组,用2去乘已经得到的素数,并将得到的数筛去(判定数组的对应下标元素标记为0)。2*2=4,筛去4

   3:3未被筛去,把3放进素数数组;遍历素数数组,用3去乘已经得到的素数,并将得到的数筛去(判定数组的对应下标元素标记为0)。3*2=6,3*3=9  筛去 6 9

   4:4被筛去,不把4放进素数数组;遍历素数数组,用4去乘已经得到的素数,并将得到的数筛去(判定数组的对应下标元素标记为0)。4*2=8 4*3=12 

   但是到此我们发现问题了,按照欧拉筛的思路,12不应该被4筛掉而应被6筛掉。

   为什么呢?

   i%prime[j]==0时,记m=i/prime[j],那么i*prime[j+1]=m*prime[j]*prime[j+1] 说明i*prime[j+1]是prime[j]的倍数。

      因此设i*prime[j+1]=k*prime[j],由于prime[j+1]>prime[j],因此k>i,所以i*prime[j+1]不应该被i筛掉而应该被k筛掉(若此时k不是最大非自身质因数,则此时的k相当于i,肯定又有下一个更大的k)。

上代码:

 

#include<stdio.h>
#define N 100000  //N为最大范围,自定。
int is_prime[N+1];
int prime[N];//素数数组大小自己估计给出
 
int main(void){
    int count=0;  //用于记录已经找到的素数的个数
 
    for(int i=2;i<=N;i++)
         is_prime[i]=1;
     
    for(int i=2;i<=N;i++){
        
        if(is_prime[i]==1)
             prime[++count]=i;
         
            //遍历素数数组,用i去乘已经得到的素数      
        for(int j=1;j<=count&&i*prime[j]<=N;j++){ // 注意&&后的条件
            is_prime[i*prime[j]]=0;
            if(i%prime[j]==0)    //******中途结束遍历的条件********关键
                break;
        }
     }
              
    for(int i=1;i<=count;i++)
        printf("%d ",prime[i]);
        
    return 0;
 }

 

  是不是听起来欧拉筛逼格很高的样子?

  但理解了 {设i*prime[j+1]=k*prime[j],由于prime[j+1]>prime[j],因此k>i,所以i*prime[j+1]不应该被i筛掉而应该被k筛掉} 这一部分,欧拉筛并不难吧!

 

         

   

 

                                                                                               

素数筛

原文:https://www.cnblogs.com/WhiteThornZzf/p/12329270.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!