首页 > 其他分享 >筛法--朴素筛法和埃式筛法和线性筛法

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

时间:2023-05-31 22:33:28浏览次数:41  
标签:埃式 筛法 -- 合数 最小 int 因子 primes

朴素筛法:

#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的,这个方就会降低算法的效率。。。。这个地方我们就要想办法优化。。。。。

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

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

 

代码如下:

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

#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:

 

 

线性筛法是对朴素筛法的进一步优化,埃式筛法的缺陷在于,对于同一个合数,可能被筛选多次。为了确保每一个合数只被筛选一次,我们用每个合数的最小质因子来进行筛选
之所以被称为线性,是因为: 11 ~ n� 之内的任何一个合数一定会被筛掉,而且筛的时候只用最小质因子来筛,每一个数都只有一个最小质因子,所以每个数都只会被筛一次,因此线性筛法是线性的.
具体操作方法为:之前每筛到一个素数,便将这个素数放入到primes[ ]中,在每次循环中,从头开始遍历primes[ ], 筛选出的合数为 primes[j]×i������[�]×�为了保证每个数只被筛选一次,就要保证每个合数都是被其最小质因子筛选掉的,即要找出 primes[j]×i������[�]×� 的最小质因子,所以我们分两种情况讨论:
首先说明:primes[j]×i������[�]×� 的最小质因子应该是 min(���(primes[j]������[�] , i� 的最小质因子),即二者中的最小值
1.当 i� % primes[j]≠0������[�]≠0 时说明此时 primes[j]������[�] 小于 i� 的所有质因子,因为 primes[j]������[�] 是从小到大进行枚举的,如果 primes[j]������[�] 是 i� 的质因子之一,那么应该满足 i� % primes[j]=0������[�]=0 才对。所以此时 primes[j]������[�] 是 primes[j]×i������[�]×�的最小质因子
2.当 i� % primes[j]=0������[�]=0 时说明此时 primes[j]������[�] 是 i� 的最小质因子(因为 primes[j]������[�] 是从小到大进行枚举的),所以此时primes[j]������[�] 也是 primes[j]×i������[�]×�的最小质因子
综上,使用 primes[j]������[�] 来筛选 primes[j]×i������[�]×� 是可行的,因为两种情况下 primes[j]������[�] 都是 primes[j]×i������[�]×� 的最小质因子
那么如何保证每个数都只被筛选一次?即循环应该在何时结束?循环应该在i % primes[ ] == 0的时候中止,理由如下:
当 i� % primes[j]=0������[�]=0 的时候如果不中止,那么将进入下一次循环,下一次循环要筛掉的数字是 primes[j+1]×i������[�+1]×� 。对于 primes[j+1]×i������[�+1]×� ,i� 的值没有变,和上一步满足 i� % primes[j]=0������[�]=0 时的 i� 是一样的,所以当前 i� 的最小质因子仍为 primes[j]������[�];但是当前为 primes[j+1]������[�+1] ,即比上一次循环的 primes[j]������[�] 要大,那么此时 primes[j+1]������[�+1] 和 i� 的最小质因子 (primes[j])(������[�]) 相比,较小值为 i� 的最小质因子 (primes[j])(������[�]) ,所以 primes[j+1]×i������[�+1]×� 的最小质因子应该为 (primes[j])(������[�]) ,那么 primes[j+1]×i������[�+1]×� 这个数应该由 (primes[j])(������[�]) 来筛选掉,但是当前却是由 (primes[j+1])(������[�+1]) 筛选掉的,所以出现了重复筛选。
因此,为了保证所有数只被筛选一次,循环需要在 i % primes[ ] == 0的时候中止
若 n<106�<106 ,线性筛和埃氏筛的时间效率差不多; 若 n>107�>107 ,线性筛会比埃氏筛快了大概一倍
作者:GodLan链接:https://www.acwing.com/activity/content/code/content/5374176/来源:AcWing著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

标签:埃式,筛法,--,合数,最小,int,因子,primes
From: https://www.cnblogs.com/FJCLJ/p/17438289.html

相关文章

  • 通用数字滤波算法
    不论你是做数字信号处理还是系统自动控制,只要系统中有模拟数据采集部分,就不可避免的存在噪声干扰的问题。应对噪声,一个方法就是利用硬件搭建模拟的滤波器,在前端采样电路滤除掉噪声;另一个方法,就是利用ADC采样,运行软件滤波算法,滤除掉信号中的噪声。特别声明:本文为个人在阅读《匠......
  • 基于模拟退火优化算法的三维装箱优化matlab仿真,优化重量利用率和空间利用率
    1.算法仿真效果matlab2022a仿真结果如下:2.算法涉及理论知识概要模拟退火算法来源于固体退火原理,是一种基于概率的算法,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减......
  • selenium教程
    一、selenium和chromedriver的安装配置1、下载seleniumpipinstallselenium2、下载chromedriverhttps://npmmirror.com/chromedriver版本和chrome版本要一致3、简单使用案例fromseleniumimportwebdriverimporttimeoptions=webdriver.ChromeOptions()#取消Ch......
  • python list 转 字典,父节点包含子节点
    list转字典,父节点包含子节点classData:def__init__(self,id,p_id,name):self.id=idself.p_id=p_idself.name=namedefconvert_to_dict(data_list):result_dict={}fordataindata_list:ifdata.p_i......
  • ES6新特性
    ES6新特性ES6官方文档:https://es6.ruanyifeng.com参考笔记:https://docs.mphy.top/#/ECMAScript6+/ch01一、ES6相关介绍ES全程EcmaScript,是脚本语言的规范,而平时经常编写的JavaScript,是EcmaScript的一种实现,所以ES新特性其实指的就算JavaScript的新特性。1.1什么是ECMAECMA(E......
  • 学习JavaSE基础-day1
    JRE和JDKJRE:Java运行环境,如果想要运行Java程序至少要安装JREJDK:Java开发环境(开发工具包),如果要开发Java程序,必须安装JDKJRE=JVM+核心类库JDK=JRE+开发工具包JDK>JRE>JVM关系如图所示: JDK下载地址:www.oracle.com配置Path环境变量:希望可以在命令窗口的任意的......
  • Revit二次开发系列教程01-如何在Revit中创建模型过程的理解
    目录01案例02步骤讲解03关键类理解04总结05源码地址01案例创建一个结构墙usingAutodesk.Revit.Attributes;usingAutodesk.Revit.DB;usingAutodesk.Revit.UI;usingSystem.Linq;namespaceExampleBasic{[Transaction(TransactionMode.Manual)][Regener......
  • 考试
    <%--CreatedbyIntelliJIDEA.User:绿波亭Date:2023/5/29Time:17:34TochangethistemplateuseFile|Settings|FileTemplates.--%><%@pagecontentType="text/html;charset=UTF-8"language="java"%><!DOCTYP......
  • 什么是监督学习
    监督学习(SupervisedLearning)是一种机器学习任务,其中算法通过从标记的训练数据中学习模式和关系,以进行预测或分类。在监督学习中,算法的目标是通过输入特征与其相应的标签之间的关联性,构建一个能够准确预测新数据标签的模型。在监督学习中,训练数据包含输入特征和相应的标签或输出......
  • qt5.15.9 静态编译 msvc 2017
    软件准备:VisualStudio2017ActivePerlPythonopenssl1.1以上版本QT5.15.9源码: https://download.qt.io/archive/qt/5.15/5.15.9/single/ 第一步命令:D:\qt-everywhere-src-5.15.9>configure.bat-prefixD:\Qt\Qt5.15.9-static-static-static-runtime-confirm-li......