筛法--朴素筛法和埃式筛法和线性筛法

这篇具有很好参考价值的文章主要介绍了筛法--朴素筛法和埃式筛法和线性筛法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

筛法--朴素筛法和埃式筛法和线性筛法

朴素筛法:

#include <iostream>
#include <algorithm>
using namespace std;
const int N=1000010;
int primes[N],cnt;
bool st[N];

void get_primes(int  n){
    
    for(int i=2;i<=n;i++){
         
        if(!st[i])//st==0 代表的是这个数是质数
        {
            primes[cnt++]=i;
        }
        for(int j=i+i;j<=n;j+=i)
        {
            st[j]=true;
        }
    }    
}
int main(){
    int n;
    cin>>n;
    get_primes(n);
    
    cout<<cnt<<endl; 
}

 这个朴素算法的思路就是,枚举这些数,首先在st数组初始化时,就是已经把这个数组内的值都初始化为0,也就是说都是看成是质数。。。。

然后,如果这个数确实是质数,那么我们就可以把这个数放入我们存质数的数组里面去,然后对质数的个数进行增加,并且,我们把这个质数的倍数(2倍,3倍,4倍。。。。。。)的st都标记为合数(st=1),范围是这个数小于n。。

但是我我们举个例子就可以发现这个朴素的算法有明显的可以优化的地方

让我们看这个代码段

   for(int i=2;i<=n;i++){


 for(int j=i+i;j<=n;j+=i)
        {
            st[j]=true;
        }


}

假如这个n是等于12

i=2    倍数有2,4,6,8,10,12  (都会被筛)

i=3     倍数有3,6,9,12  (都会被筛)

i=4     倍数有4,8,12   (都会被筛)

............

从这个中间我们可以看到4,6,8和12是多次被赋值为true的,这个方就会降低算法的效率。。。。这个地方我们就要想办法优化。。。。。

就有一种想法就是:我们不要把所有数的倍数都进行一个赋值,而是有一部分的数进行倍数的赋值------这个数就是质数

也就是说我们对质数的倍数进行赋值----也就是一个埃及人发明的。。---------我们称之为埃式筛法

筛法--朴素筛法和埃式筛法和线性筛法

 文章来源地址https://www.toymoban.com/news/detail-466189.html

代码如下:

也就把这个代码段放入循环中:

#include <iostream>
#include <algorithm>
using namespace std;

const int N=1000010;
int primes[N],cnt;
bool st[N];

void get_primes(int  n){
    
    for(int i=2;i<=n;i++){
         
        if(!st[i])//st==0 代表的是这个数是质数
        {
            primes[cnt++]=i;
//。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
for(int j=i+i;j<=n;j+=i) { st[j]=true;

// 这个是被放进来的部分 }
//。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 } } }
int main(){ int n; cin>>n; get_primes(n); cout<<cnt<<endl; }

 在上面的埃式筛法中,我们做到了一个优化,但是这个优化也不是很完全:

还是举这个例子来说,n=12时

i=2    倍数有2,4,6,8,10,12   (都会被筛)

i=3     倍数有3,6,9,12   (都会被筛)

i=4     倍数有4,8,12       但是根据算法的思路,我们可以知道,4不是质数所以他的倍数不会被筛,就是这种地方优化了算法的效率。。

 

然后就是线性筛法,比埃式筛法更快原因是:做到了每一个数都只被筛一遍-----也就是每一个数只会被他的最小的质因子去筛掉(核心)

#include <iostream>
#include <algorithm>
using namespace std;

const int N=1000010;
int primes[N],cnt;
bool st[N];

void get_primes(int  n){
    
    for(int i=2;i<=n;i++){//外层循环是枚举所有的数的
  
     if(!st[i]) primes[cnt++]=i;
     for(int j=0;primes[j]<=n/i;j++)
     //j是从小到大枚举所有的质数 
     
     {
         st[primes[j]*i]=true;
         if(i%primes[j]==0) break;
     }
}
}
int main(){
    int n;
    cin>>n;
    get_primes(n);
    
    cout<<cnt<<endl;
        
}

........................................................................................................

引用:(这个地方是没看懂他怎么想的,但是这个例子部分还是很明显易懂的)

欧拉筛法任何一个合数都可以写成几个质数相乘的形式,也就是任何一个合数至少有一个最小质因子,而每一个质数的倍数一定不是质数,所以我们可以通过每一个合数的一个最小素因子去筛掉它,并且只筛掉一次,这样就把时间复杂度降到了O(n)。其中就是这条语句起着至关重要的作用:if(i%prime[j]==0)break;为什么呢?举个栗子:假设当前i=9,prime素数表里有2,3,5,7,即先筛掉2*9=18,然后筛掉3*9=27,判断9%3==0,退出当前循环。因为9=3*3,如果继续筛掉5*9,相当于筛3*3*5=3*15,而这个数在i=15时筛掉就可以了。因此,可以用以下的推导证明上面的结论:假设pi是当前i的最小质因子,那么素数表有p1<p2<...<pi<pi+1<...,枚举当前质数表p[j],j从小到大枚举,直到i%p[j]==0这个条件时,前面所有质数p[j]都是i*p[j]的最小质因子,如果继续枚举下去,即下一个要被筛掉的合数为i*prime[j+1],因为i中已经含有一个最小质因子prime[j],设i=k*prime[j],则i*prime[j+1]=k*prime[j]*prime[j+1]=k'*prime[j],显然k'>i,k'*prime[j]可以留在后面被筛掉。换句话说,prime[j+1]已不是i*prime[j+1]的最小质因子,i*prime[j+1]必然可以被一个更小的质数prime[j]筛掉,那么k'=i*prime[j+1]/prime[j]=k*prime[j+1]就一定比i大,即下一次i遍历到i=k'时,k'*prime[j]却被其最小素因子prime[j]筛掉,而不是prime[j+1],这就相当于再筛选了一次(增加了时间复杂度),所以只需将j遍历到i的最小素因子prime[j]就可以了,即保证每一个合数只被其最小质因子筛掉,这样把时间复杂度完美地从O(nloglogn)降到了O(n)。

................................................................................................................

根据自己的分析:

我们用每个合数的最小质因子来进行筛选之所以被称为线性,是因为: 1-n之内的任何一个合数一定会被筛掉,而且筛的时候只用最小质因子来筛,

每一个数都只有一个最小质因子,所以每个数都只会被筛一次,因此线性筛法是线性的。

具体的步骤就是:之前每筛到一个素数j,就将这个素数放入到primes[]中,在每次循环中,从头开始遍历primes[],筛选出的合数就是primes[j]*i,

为了保证每个数只被筛选一次,就要保证每个合数都是被其最小的质因子筛选掉的,所以我们就要找到primes【j】*i的最小质因子,所以我们需分情况讨论:

首先我们知道,primes【j】*i 的最小质因子应该是min(primes【j】,i的最小质因子),也就是说我们需要找到二者中的最小值。。。。。

1.当i%primes[j]不等于0时

此时primes[j]小于i的所有质因子,因为primes[j]是从小到大进行枚举的,如果primes[j]是i中质因子之一,那么就会满足i%primes[j]等于0。

所以我们就可以知道了i的质因子还在后面,还没有加入到我们的primes数组中,所以也就是说,primes【j】就是i*primes【j】的最小质因子

------------所以这种情况下,primes[j]是可以用来筛这个合数 primes【j】*i 的。。。。

2. 当i%primes[j]等于0时

说明此时,primes【j】就是i的最小质因子,原因就是:primes[j]是从小到大进行枚举的,这个时候,很明显我们就可以知道primes【j】就是i*primes【j】的最小质因子

综合以上的情况我们就可以知道用这个primes[j]是可以用来筛这个合数 primes【j】*i 是合理的,因为在这两种情况下,primes【j】就是i*primes【j】的最小质因子。。。。

如何保证这个数只被筛一次,也就是说我们里面这个内循环,每次只能筛一个数走,也就是说,只有一个地方会被设置为true每一次。

所以一旦这个primes里的质数筛走一个数之后就要内循环break,这个时候我们就需要找到这个break的条件。。。

循环应该在i % primes[ ] == 0时停止。。。。。

在一些测试的数据上,如果n<1e6,线性筛和埃式筛的时间效率差不多,如果n>1e7的时候,线性比埃式要快了一倍左右。。。。

1e6:

筛法--朴素筛法和埃式筛法和线性筛法

 1e7:

筛法--朴素筛法和埃式筛法和线性筛法

 

 

线性筛法是对朴素筛法的进一步优化,埃式筛法的缺陷在于,对于同一个合数,可能被筛选多次。为了确保每一个合数只被筛选一次,我们用每个合数的最小质因子来进行筛选

筛法--朴素筛法和埃式筛法和线性筛法

 

到了这里,关于筛法--朴素筛法和埃式筛法和线性筛法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 计算机视觉与深度学习 | 非线性优化理论:图优化、高斯牛顿法和列文伯格-马夸尔特算法

    ===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 计算机视觉与深度学习 | SLAM国内外研究现状 计算机视觉与深度学习 | 视觉惯性SLAM的基础理论 计算机

    2024年02月08日
    浏览(39)
  • C++数论————质数筛法(单独判断一个数,判断N个数) 埃氏筛法

    质数想必大家都不陌生 从小学到大 质数的概念: 一个数如果除了1和本身之外没有其他的因子,那么这个数被称为质数 今天要讲两个知识点: 在C++中如何判断一个数是否为质数 在C++中如何判断1-N之间哪些数为整数 在C++中如何判断一个数是否为质数 这个知识点较为简单 充分

    2023年04月08日
    浏览(38)
  • 欧拉算法与埃氏筛法比较

       

    2024年02月12日
    浏览(32)
  • [素数筛][容斥原理]:埃拉托斯特尼筛法

    求解问题:不超过一个给定正整数N的素数的个数 方法介绍: 根据合数的性质:一个合数可以被一个不超过它的平方根的素数整除 这里举例N=100: 介绍:为了找出不超过100的素数个数,首先根据合数的性质可以知道: 100的合数一定有一个不超过10的素因子。 因为小于10的素数

    2023年04月08日
    浏览(30)
  • 湘大 XTU OJ 1345 素数字符串 题解:欧拉筛法 前缀和 ‘\0‘ sprintf

    素数字符串 我们将素数从小到大依次书写,可以得到一个字符串\\\"23571113⋯\\\",已知一个数码d(0≤d≤9),求字符串在区间[L,R]之间的多少个d? 第一行是一个整数T(1≤T≤10000),表示样例的个数。 每个样例是一行, 为3个整数,区间L,R,(1≤L≤R≤1000000)和数码d。 区间从1开始计数。 每

    2024年02月12日
    浏览(25)
  • 1. 大端法和小端法

    一个 int32_t 是4个字节,在内存中的存储是高位字节在低地址,低位字节在高地址。 (数字)前者的高低是数字位数的高低,左边是高位数,右边是低位数; (地址)后者的高低是内存中的地址的大小,大的值就是高地址。 大端法: 小端法: 网络程序要考虑字节序的问题。

    2023年04月17日
    浏览(34)
  • POJ 2429 Miller-rabin素数判定 + pollard-rho质因子分解 + 埃氏筛法

    题目不能说是很难,只是用到了许多数学上的知识(费马小定理,miller-radin,pollard-rho),还有一些算法上的知识DFS,辗转相除。 我也很菜,一个周末的时间都用在这个题目上了,但写了很多很多的注释,花费了大量的篇幅,浅谈了我对这些算法的拙见,希望能够帮助大家!

    2024年02月13日
    浏览(27)
  • 单纯形法和单纯形表法

    单纯形法(Simplex Method)是一种线性规划算法,用于求解线性规划问题。它是由乔治·达内(George Dantzig)于1947年发明的,是现代数学编程的里程碑之一。单纯形法基于线性规划问题的几何特性,通过逐步移动可行域的角点(即“单纯形”),找到最优解。 单纯形法的基本思

    2024年02月11日
    浏览(37)
  • 考研数学中放缩法和无穷项求和

    本文以例子为切入,对一些常用的放缩方法进行总结归纳,以期让读者对相关问题有一定的应对手段。 问题 :2020年高数甲,选择题第1题。 lim ⁡ n → + ∞ ( 2 n 2 + 4 n 2 + 1 + ⋯ + 2 n n 2 + n + 1 ) lim_{nto+infty}left( frac{2}{n^2}+frac{4}{n^2+1}+cdots + frac{2n}{n^2+n+1}right) n → + ∞ lim ​

    2024年02月08日
    浏览(39)
  • 头插法和尾插法建立单链表详解与实现

    写在前面:本文使用C语言和C++引用,学C和C++的同学都是可以看懂的,C++毕竟向下兼容C。很详细,一篇能搞懂代码和原理。 先来了解几个简单概念 单链表就是线性表的链式存储; 头结点:单链表在第一个结点之前附加了一个结点,这个结点里面没有存放我们要使用的数据,

    2024年02月08日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包