素数基本(埃氏筛法/线性筛法)

一、检查n是否为素数

  

最简单思路:所有可能的因数全部试一遍。

 

int gg(int n)
{
    for(int i=2;i<n;i++){
        if((n%i)==0)return 0;//有因数就不是素数咯
    }
    return 1;
}

 

进一步思考:没必要枚举所有的数,每一个小于n^(1/2)的因数i,一定有一个大于n^(1/2)的因数j与之对应,也就是i*j=n,所以枚举小于等于n^(1/2)的因数即可

 

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

 

二、约数枚举

 上面已经说过,不需要枚举所有因数,枚举出某小因数以后算出对应的大因数即可。

vector<int> gg(int n)
{
    vector<int> a;
    for(int i=2;i*i<=n;i++){
        if((n%i)==0){
            a.push_back(i);
            if((n/i)!=i)a.push_back(n/i);//根号n的情况不要重复添加
        }
    }
    return a;
}

 

三、埃氏筛法

 只对一个整数操作,O(N),已经足够了,如果对许多整数进行素性检测,还有更高效的算法,比如埃氏筛法。

问题:枚举n以内所有素数

操作:先把所有整数列出来,然后把2的倍数全部剔除,然后是三的,以此类推,遍历所有素数,把倍数全部划去。

对于每个数字i,如果没被划去,他一定是素数,因为他不是任何2到i-1数字的倍数。然后就开始划它的倍数就好。

int a[maxx];
int b[maxx+1];
int gg(int n)
{
    int p=0;//记录素数个数
    for(int i=0;i<n+1;i++)b[i]=1;
    b[0]=0;
    b[1]=0;
    //准备完毕
    for(int i=2;i<=n;i++){
        if(b[i]){
            a[p++]=i;//记录素数和个数
            for(int j=2*i;j<=n;j+=i)b[j]=0;//剔除倍数
        }
    }
    return p;//返回素数个数
}

四、区间筛法

 给定整数a和b,请问区间[a,b)内有多少个素数? 

思路:之前说过,因为b以内合数的最小质因数一定不超过sqrt(b),如果有sqrt(b)以内的素数表的话,就可以把筛选法用在[a,b)上了,先分别做好[2,sqrt(b))的表和[a,b)的表,然后从[2,sqrt(b))的表中筛得素数的同时,也将其倍数从[a,b)的表中划去,最后剩下的就是区间[a,b)内的素数了。

//不gg了,这次就来个标准一点的吧
typedef long long ll;
bool is_prime[maxn];
bool is_prime_small[maxn];
void segment_sieve(ll a,ll b) 
{
     for(ll i=0;i*i<b;++i) is_prime_small[i]=true; //初始化
     for(ll i=0;i<b-a;++i) is_prime[i]=true; //初始化,注意下标变化,为了省空间
     for(ll i=2;i*i<b;++i) {
         if(is_prime_small[i]) {
             for(ll j=2*i;j*j<b;j+=i) is_prime_small[j]=false;  //筛选[2,sqrt(b));
             //(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;
         }
     }
}

五、线性实现

筛法很多数被处理了不止1遍,比如6,在素数为2的时候处理1次,为3时候又处理一次,因此又造成了不必要处理。O(nloglogn)已经基本可以满足一般需要了。

本代码保证每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次,所以时间复杂度是O(n)

证明略

话不多说,上板子

#include<cstdio>
#include<cstring>
#define MAXN 100005
#define MAXL 1299710
int prime[MAXN];
int check[MAXL];
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;//******
    }
}

素数基本就这些内容咯。。。。

埃氏筛法(Eratosthenes筛)和线性筛(Linear Sieve)都是用于素数的算。它们的主要区别在于筛选过程中的操作不同。 1. 埃氏筛法埃氏筛法是一种古老的素数的算,由古希腊数学家埃拉托斯特尼(Eratosthenes)提出。它的基本思想是从2开始,将所有2的倍数标记为非素数,然后找到下一个未被标记的数(即3),将所有3的倍数标记为非素数,依此类推。最后留下的未被标记的数就是素数。 C++实现埃氏筛法的代码如下: ```cpp #include <iostream> #include <vector> using namespace std; const int N = 1000000;vector<int> is_prime(N, true); void sieve_of_eratosthenes() { is_prime = is_prime = false; for (int i = 2; i * i < N; ++i) { if (is_prime[i]) { for (int j = i * i; j < N; j += i) { is_prime[j] = false; } } } } int main() { sieve_of_eratosthenes(); for (int i = 2; i < N; ++i) { if (is_prime[i]) { cout << i << " "; } } return 0; } ``` 2. 线性筛线性筛是一种改进的埃氏筛法,它将筛选过程从平方根优化到线性时间。线性筛基本思想是对于每个素数p,筛选出所有小于等于p^2的合数。这样可以减少筛选的次数,提高效率。 C++实现线性筛的代码如下: ```cpp #include <iostream> #include <vector> using namespace std; const int N = 1000000; vector<int> is_prime(N, true); vector<int> primes; void linear_sieve() { is_prime = is_prime = false; for (int p = 2; p * p < N; ++p) { if (is_prime[p]) { for (int i = p * p; i < N; i += p) { is_prime[i] = false; } } } for (int p = 2; p < N; ++p) { if (is_prime[p]) { primes.push_back(p); } } } int main() { linear_sieve(); for (int i = 0; i < primes.size(); ++i) { cout << primes[i] << " "; } return 0; } ``` 这两种算都可以有效地素数,但线性筛相对于埃氏筛法在筛选次数上有很大优势。
评论 212
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兔老大RabbitMQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值