zoukankan      html  css  js  c++  java
  • 线性筛素数

    何为线性筛法,顾名思义,就是在线性时间内(也就是O(n))用筛选的方法把素数找出来的一种算法,没用过线性筛素数法的人可能会奇怪,用遍历取余判定素数不是也是线性时间的吗,没错,但是确切的说线性筛法并不是判定素数的,而是在线性时间内求出一个素数表,需要判定是否是素数的时候只要看该数是否在表内就可以瞬间知道是不是素数。

    比如想求10000以内的素数,定义表int a[10000],进行线性筛选后,a[n]的值就代表n是不是素数,a[n]如果是1,就代表n是素数,a[n]如果是0,就代表n不是素数,这就是查表。再判定其他的素数也是一样,不用再做任何计算。

    而如果用遍历取余,那么每判定一个数都要从头开始再遍历一遍,而线性筛法只在开始一次性运算完,以后只要查表即可,查表通常只需要1条语句。所以如果你的程序从始至终只需要判定那么几次素数那么用遍历取余即可,但是如果需要多次判定素数,而且这个数还不是很小的话,那么线性筛法就会体现出巨大的优越性来。

    该算法的核心为:每个合数必有一个不是他本身的最大因子,那么如果我们用这个最大的因子去筛掉这个合数的话,那么这个合数仅仅只会被筛掉一次,因为一个数的最大因子是唯一的。我们设这个合数为a,它的最大因子为i,那么存在一个整数q使得:a=i*q .设i=p1*p2*p3*....pn,且p1到pn都是素数,那么q必定是小于等于p1的一个素数。这样当i一定时,用所有符合条件的q乘上i,得到所有符合条件的a,把这样的a全部筛去,就能不重复,不遗漏的线性筛素数。

    证明

    同样用以上的设定,则有a=i*q=p1*p2*p3*....pn*q,先证明q是一个质数,假设q不是一个质数,则设q=b*c,则b*i一定也是a的因子且一定比i大,与i是a的最大因子相矛盾,所以q一定是一个质数;
    在证明q一定小于等于p1,假设q大于p1,且p2*p3*....pn*q一定是a的一个因子,又因为p2*p3*....pn*q>p1*p2*p3*....pn,所以p2*p3*....pn*q>i,与i是a的最大因子矛盾。

    最后我们就可以得出结论:对于每一个数i,乘上小于等于i的最小素因数的素数,就得到以i为最大因数的合数。设有一个数t,只要将所有以比t小的数为最大因数的合数筛去,那么比t小的数里剩下的就只有素数了。这就是线性筛法求素数的方法。

    本文给出的证明方法并不是很严格,很不严密,但是本文只是想解释线性筛素数的算法,并不是想严格证明,如果想看严格证明请看数论中的证明。另上面提到的线性筛素数的另一种说法,其实到这里读者应该差不多明白了,任何一个合数都可分解为一个素数和另一个数(不一定是素数还是合数)的乘积。我们既然找到了这个合数最大的因数,那么根据上面结论里另一个乘上的素数必然就是他的最小素因数。有很多博客说是用最小质因子来筛素数,但博主认为这样不大好证明,到了这里读者也会发现其实和最大因子是一样的。

    编程实现:

    题目 洛谷P5736 【深基7.例2】质数筛https://www.luogu.com.cn/problem/P5736

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #include<deque>
    #include<cstdlib>
    #include<ctime>
    #define dd double
    #define ll long long
    #define ull unsigned long long
    #define N 101
    #define M 100001
    using namespace std;
    
    int n;
    int a[N],pr[M],tail;
    int vis[M];
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    	
    	vis[1]=1;
    	for(int i=2;i<=M;i++)
    	{
    		if(!vis[i]) pr[++tail]=i;
    		for(int j=1;j<=tail&&pr[j]*i<M;j++)
    		{
    			vis[pr[j]*i]=1;
    			if(i%pr[j]==0) break;
    		}
    	}
    	
    	for(int i=1;i<=n;i++) if(!vis[a[i]]) printf("%d ",a[i]);
    	
        return 0;
    }
    
  • 相关阅读:
    java获取两个日期之间的所有日期
    java实现https免证书认证
    Linux系统下安装rz/sz命令及使用说明
    Linux 7 关闭、禁用防火墙服务
    Linux下iptables 禁止端口和开放端口
    JBPM4 常用表结构及其说明
    mysql索引使用技巧及注意事项
    MySQL的btree索引和hash索引的区别
    https://www.cnblogs.com/
    Git+Gitlab+Ansible剧本实现一键部署动态网站(二)--技术流ken
  • 原文地址:https://www.cnblogs.com/TianMeng-hyl/p/13460903.html
Copyright © 2011-2022 走看看