【算法学习】线性筛素数

声明:本文所涉及部分内容可能并非原创。如发现本文侵犯了您的权益,可申请博主删除。

嘛……好久没有写博客了,今天无聊来写一篇~

为什么要写这个呢?因为这个算法让我知道了我什么都不会啊……

闲话少说,切入正题。

1. 筛素数

素数即质数,定义就不说啦

那么我们经典的筛素数的方法呢都很简单,开一个bool数组,从1到$\sqrt n$,如果是素数就把这个数的倍数给筛掉。

时间复杂度显然:$O(nlog_n)$

代码也很简单:

void prime(int n) {
    for(int i = 2; i * i<= n; i++)
        if(!p[i])
            for(int j = i+i; j <= n; j += i) p[j] = true;
}
//p[i]=false:该数是素数;p[i]=true:该数不是素数 

//很久以前写的,风格比较迥异QWQ

但是显然,这样的筛素数效率并不高。比如12,在2*6时被筛了一次,在3*4时又被筛了一次。

显然筛素数的算法是有优化空间的。于是,便有了线性筛素数

2. 线性筛素数

又叫欧拉筛法。那么这种方法是怎样提高筛素数的效率的呢?

我们知道,每个合数都是由若干个质数乘积组成的,故都有一个最小的质因子。(如6=2*3,2就是6的最小质因子)

用这个因子把合数给筛掉,可以达到线性时间。

具体来看一下代码:

void prime_2()
{
    for(int i=2;i<=n;i++)
    {
        if(!p[i]) prime[++ind]=i;
        for(int j=1;j<=ind;j++)
        {
            if(i*prime[j]>n) break;
            p[i*prime[j]]=true;
            if(i%prime[j]==0) break;
        }
    }
}

代码其实也很简单,查素数表,把i*prime[j]给删掉……

等等,这句话是什么意思?

if(i%prime[j]==0) break;

其实这句话正体现了之前的思想:用最小的质因子把合数给筛掉

prime是顺序枚举的,当prime[j]第一次是i的倍数时,说明prime[j]是i的最小质因子。

那么prime[j+1]*i一定会被prime[j]乘以某个数给筛掉。

还是举个6的例子吧。当素数枚举到2时6*2=12,把12筛掉,6%2==0,即2是6的最小质因子。那么3就没有必要再枚举了,6*3=18=9*2,2会“利用”9把18筛掉。

为什么一定会被筛掉呢?6=2*3,18=2*3*3,故18一定会被2*某个数筛掉,而没有必再用3*某个数再重复筛一遍。

而枚举到9时,2不是9的最小质因子,所以9*2=18,2一定是18的最小质因子。

这也是线性筛素数效率高的重要原因之一! (事实上,删掉这句话对最终答案没有影响,但是效率会大大降低)

理解了上面这行代码,基本就掌握了线性筛素数。

时间复杂度自然同算法名称,是$O(n)$啦

3. 例题

3.1 问题

定义$p(x)$为$x$除1外最小的约数,定义$f(n)=\sum_{i=2}^n p(i)$

给定$n$($2≤n<≤5*10^7$),求$f(n)$的值。

3.2 问题求解

显然这题如果用传统的筛法肯定会TLE,于是可以用线性筛求解。

在原来的线性筛基础上加2行代码就可以了。

1. 如果该数是质数,累加进ans

2. 把筛掉合数的质数累加进ans

3.3 代码

void work()
{
    for(int i=2;i<=n;i++)
    {
        if(!p[i])
        {
            ans+=i;
            prime[++ind]=i; //1.
        }
        for(int j=1;j<=ind;j++)
        {
            if(i*prime[j]>n) break;
            p[i*prime[j]]=true;
            ans+=prime[j]; //2.
            if(i%prime[j]==0) break;
        }
    }
}

4. 吐槽

为何要写这个文章呢?

因为这题是一道考题,而蒟蒻的博主并不知道还有这种操作,于是只打了暴力……

最关键的是开数组时多加了俩0,硬生生的超了512M的内存限制……

QAQ

时间: 2024-10-18 18:25:16

【算法学习】线性筛素数的相关文章

睡前数学一小时之线性筛素数:

睡前数学一小时之线性筛素数:1,朴素的筛素数算法:埃拉托斯特尼筛法.这是个简单再简单不过的一个素数的筛法.只是名字很拉风.这就告诉我们,往往东西不好这没什么,名字很拉风.别人也不会记住.hhhhh.这个的思路就是.每一个数都是由一个质数与和数(质数也可以)的积组成.这也是质数与和数的定义.而这个它这个筛发,就是当遇到一个质数的时候开始枚举,枚举[1,n]中间关于这个质数的倍数.每次都枚举,每次都将算出的这个数打上标记.而最后整个区间内的质数枚举完后,整个区间内的质数也就筛选出来了.这个很简单.时

洛谷 P3383 【模板】线性筛素数

P3383 [模板]线性筛素数 题目描述 如题,给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内) 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示查询的范围和查询的个数. 接下来M行每行包含一个不小于1且不大于N的整数,即询问概数是否为质数. 输出格式: 输出包含M行,每行为Yes或No,即依次为每一个询问的结果. 输入输出样例 输入样例#1: 100 5 2 3 4 91 97 输出样例#1: Yes Yes No No Yes 说明 时空限制:5

线性筛素数模板

传送门:线性筛素数 Prime: 1 #include<cstdio> 2 3 const int MAXN = 10000100; 4 int Prime[MAXN],n,m,Size; 5 bool Vis[MAXN]={1,1}; 6 7 int main() 8 { 9 scanf("%d%d",&n,&m); 10 for(int i=2;i<n;i++) 11 { 12 if(!Vis[i]) 13 Prime[++Size]=i; 14

leetcode 204. Count Primes(线性筛素数)

Description: Count the number of prime numbers less than a non-negative number, n. 题解:就是线性筛素数的模板题. class Solution { public: int countPrimes(int n) { int ans=0; vector<int>is_prime(n+1,1); for(int i=2;i<n;i++){ if(is_prime[i]){ ans++; for(int j=2*

线性筛素数(欧拉筛)

线性筛素数(欧拉筛) 欧拉筛为啥是\(O(n)\)的呢?我们先来看看代码. #include <cstdio> using namespace std; const int maxn=10000000; int n, m, prime[maxn], isnt_prime[maxn], tot; void get_prime(int n){ isnt_prime[0]=isnt_prime[1]=1; for (int i=2; i<=n; ++i){ //当前数是所有数小于n的数而不只是

线性筛素数(欧拉筛)+前缀和优化

关于素数的定义:在大于1的自然数中,除了1和它本身以外不再有其他因数. 判断一个数是否是素数: 1 int x; // 要求的数 2 for(int i=2;i<=sqrt(x);++i) 3 { 4 if(x%i==0) 5 { 6 cout << "这不是素数" << endl; 7 break; 8 } 9 } 埃氏筛法(时间复杂度:$O(NloglogN)$): 1 int num_prime = 0; // 素数的数量 2 int prime[5

线性筛素数详细整理

如果你在1个月前让我判断素数,我一定会猛敲出以下代码: bool check( int num ) { int tmp =sqrt( num); for(int i= 2;i <=tmp; i++) if(num %i== 0) return 0 ; return 1 ; //实在是太慢了! } $ $ 下面给大家带来3种筛选素数和一种直接判断素数 $ $ $ $ 什么是线性筛? 对于求多个质数时与其一个个判断不如用排除法,用空间换取大量时间. $ $ $ $ $ $ 一般筛法(埃拉托斯特尼筛法

单纯的线性筛素数

很多地方要用到素数,而能很快的写出代码筛出素数是很不错的我就单独写一个线性筛的代码和证明. #include<iostream> #incldue<cstdio> #include<queue> #include<algorihtm> #include<cstding> using namespace std; #define N 1000009 bool mark[N];//标记合数, int prime[90000];//储存质数 void

线性筛素数、欧拉函数

判断一个数n是否是素数,众所周知可以用O(sqrt(n))的方法. 但是如果要求很多个数,这个方法就不太好了.(比如所有小于n的数,复杂度就是O(n1.5).) 埃拉托斯特尼筛法,大家都听说过.从2到n,去掉每个数的倍数,剩下来的就是质数. 不过这个方法会重复删除,比如6是2.3的倍数,会被删2次,因子越多,删的次数就越多. 改进之后的线性筛保证每个数只被最小的质因子删,所以是O(n)的. #include<cstdio> #include<cstring> #define MAX