浅谈筛素数

假设一个数N,我们现在要求1-N之间哪些数是素数,那些不是素数?

 下面介绍几种解法(当然打表就免了吧)

(1)最简单最暴力,也是最慢的解法:

  枚举 2-N/2 之间的所有数试试可不可以与 N 相除没有余数。

  有人说:为啥不枚举N。-----自己悟吧;

#include<stdio.h>
int main()
{
    bool flag;
      int n;
      while(scanf("%d",&n)==1)
      { 
          for(i=2;i<=n/2;i++)if(n%i==0)
          {
              flag=1;
              break;
          }
        if(flag==0)printf("YES\n");
        else  printf("NO\n");
      }
}

(2)最普通最常见的解法:

  枚举 2-sqrt(N) 之间的所有数试试可不可以与 N 相除没有余数。

  为什么要枚举到sqrt(N)呢?

  证明:因为对一个数n,如果他能分解成n=pq,那么pq里必然有一个大于等于根号n一个小于等于根号n,也就是说一个合数必然有一个因子是小于等于根号n的,所以对一个数n,只要检验他有没有小于等于根号n的因子就可以了。

  所以代码就好写了.

#include<cstdio>
#include<cmath>
using namespace std;
int main()
{
    bool flag;
    int n,x;
    while(scanf("%d",&n)==1)
    {
        x=sqrt(n);
        for(i=2; i<=x; i++)
            if(n%i==0)
            {
                flag=1;
                break;
            }
        if(flag==0)printf("YES\n");
        else  printf("NO\n");
    }
}

 

(4)普通筛选法--埃拉托斯特尼筛法:

  听名字,哇哦,如此高大上,一定特别好用吧;

  WOC!枚举每一个质因子,能与它乘的都记录为不是。

  基本思想:素数的倍数一定不是素数
  
实现方法:用一个长度为N+1的数组保存信息(0表示素数,1表示非素数),先假设所有的数都是素数(初始化为0),从第一个素数2开 始,把2的倍数都标记为非素数(置为1),一直到大于N;然后进行下一趟,找到2后面的下一个素数3,进行同样的处理,直到最后,数组中依然为0的数即为素数。

  说明:整数1特殊处理即可。

  prime[]用来保存得到的素数 prime[] = {2,3,5,7,11,.........} tot 是当前得到的素数的个数 check :0表示是素数  1表示合数。

memset(check, 0, sizeof(check));
int tot = 0;
for (int i = 2; i <= n; ++i)
{
  if (!check[i])
  {
    prime[tot++] = i;
  }
  for (int j = i+i; j <= n; j += i)
  {
    check[j] = 1;
  }
}

 

  目测或者手动模拟的话,明显有许多冗余(就是重复的操作),会消耗不少时间。

  有没有啥其他的改进方法呢?----下面有请出我们的欧拉吧。

(4)欧拉线性筛:

#include<cstdio>
#include<cstring>
using namespace std;
int prime[];
int check[];
int tot = 0;
memset(check, 0, sizeof(check));
for (int i = 2; i < MAXL; ++i)
{
    if (!check[i])prime[tot++] = i;
    for (int j = 0; j < tot; ++j)
    {
        if (i * prime[j] > MAXL)break;    //超过了要所求的数,计算就没必要了; 
        check[i*prime[j]] = 1;            //可以被乘出来,就一定不是素数咯。 
        if (i % prime[j] == 0)break;    //这个数被它%等于0了,说明已经出现了重复,break。 
    }
}
// check中没有被标记过的就是素数咯。 

prime[]数组中的素数是递增的,当i能整除prime[j],那么i*prime[j+1]这个合数肯定被prime[j]乘以某个数筛掉。因为i中含有prime[j],prime[j]比prime[j+1]小,即i=k*prime[j],那么i*prime[j+1]=(k*prime[j])*prime[j+1]=k’*prime[j],接下去的素数同理。所以不用筛下去了。因此,在满足i%prime[j]==0这个条件之前以及第一次满足改条件时,prime[j]必定是prime[j]*i的最小因子。

 

posted @ 2018-04-14 16:27  Manjusaka丶梦寒  阅读(150)  评论(0编辑  收藏  举报