ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

素数筛选

2019-08-21 19:53:21  阅读:223  来源: 互联网

标签:prime int ll flag 素数 1e 筛选


经典:

int isprime(int n)
{
  int i;
  if(n<=1) return 0;
  for(i=2;i<=sqrt(n);i++)
    if(n%i==0) return 0;
  return 1;
}

显然如果要判断一定范围内的素数,这种算法很慢。

埃拉托斯特尼(Eratosthenes)筛法

int flag[maxn+5]={1,1};  //if(flag[i]=0) i为素数
void isprime()
{
  int i,j;
  memset(flag,0,sizeof(flag));
  for(i=2;i<=sqrt(N);i++)
  {
    if(!flag[i])
    {
      for(j=i*i;j<=N;j+=i)
        flag[j]=1;
    }
  }
}

欧拉(Euler)筛法

int flag[maxn+5]={1,1},prime[maxn+5];  //if(flag[i]=0) i为素数
void isprime()
{
  int i,j,cnt=0;
  memset(flag,0,sizeof(flag));
  for(i=2;i<=maxn;i++)
  {
    if(!flag[i])  prime[cnt++]=i;
    for(j=0;j<cnt&&prime[j]*i<=maxn;j++)
    {
      flag[prime[j]*i]=1;
      if(i%prime[j]==0) break;
    }
  }
}

为什么当 i|prime[j] 时,就可以跳出循环?

,令i|prime[j]=m,所以,可以看出i*prime[j+1]可以由k'*prime[j]得到,又因为prime[j+1]>prime[j],所以k'肯定比此时的i大,也就是说i*prime[j+1]可以在以后被更新,所以此时不用,以免重复计算。

区间素数筛

引:给定整数 a 和 b,请问区间 [a,b) 内有多少个素数?(a<b≤10^12,b−a≤10^6)

因为 b 以内合数的最小质因数一定不会超过√b ,因为如果存在 d 是 n 的约数,那么 n/d 也是 n 的约数,由 n=d×n/d 可知 min(d,n/d)≤√n。

如果有√n以内的素数表的话,就可以把埃式筛法用在[a,b)上了。也就是说,先分别做好[2,√b)的表和[a,b)的表,然后从[2,√b)的表中筛得素数的同时,也将其倍数从[a,b)的表中筛去,最后剩下的就是区间[a,b)内的素数了。

假如要求[1e^9,1e^9+2)区间内的素数,难道我们要开1e^9+3 大小的数组吗?其实并不用,我们利用下标偏移,可以减少空间开销。即将[1e^9,1e^9+2)对应到[0,2),整体区间向左偏移 1e^9。

说白了,就是用已知小范围素数更新未知大范围素数,然后利用下标偏移维护区间。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5;
bool is_prime[MAXN],is_prime_small[MAXN];
ll prime[MAXN],prime_num = 0;
//对区间[a,b)内的整数执行筛法,is_prime[i-a]=true <=> 表示i是素数(下标偏移了a)
void segment_sieve(ll a,ll b) 
{
  for(ll i=0;i*i<b;i++) is_prime_small[i]=true;//对[2,sqrt(b))的初始化全为质数
  for(ll i=0;i<b-a;i++) is_prime[i]=true;//对下标偏移后的[a,b)进行初始化
  for(ll i = 2; i * i < b; i++) //筛选[2,sqrt(b))
  { 
    if(is_prime_small[i]) 
    {
      for(ll j=2*i;j*j<b;j+=i) 
        is_prime_small[j]=false;
      
      //(a+i-1)/i 得到最接近a的i的倍数,最低是i的2倍,然后筛选
      for(ll j=max(2LL,(a+i-1)/i)*i;j<b;j+=i)
        is_prime[j-a]=false;
    }
  }
  for(ll i=0;i<b-a;i++) //统计个数 下标偏移
    if(is_prime[i])
      prime[prime_num++]=i+a;
}
int main() 
{
  ll a,b;
  while (~scanf("%lld%lld",&a,&b)) 
  {
    prime_num=0;
    memset(prime,0,sizeof(prime));
    segment_sieve(a,b);
    printf("%lld\n",prime_num);
  }
  return 0;
}

标签:prime,int,ll,flag,素数,1e,筛选
来源: https://www.cnblogs.com/VividBinGo/p/11388481.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有