zoukankan      html  css  js  c++  java
  • 筛法求素数

    要解决的问题:给定一个正整数(n),求出所有小于等于(n)的素数

    素数又称质数,其因子只有1和它自己本身;与素数相对的叫合数

    1、埃拉托斯特尼筛法

    #include<bits/stdc++.h>
    using namespace std;
    #define N 10000
    #define ll long long 
    int visited[N];
    int prime[N];
    int num=0;
    // 埃式筛 
    void cal(int n)
    {
    	memset(visited, 0, sizeof(visited));
    	memset(prime, 0, sizeof(prime));
    	for(int i=2; i<=n; ++i){ // 1既不是质数也不是合数 
    		if(!visited[i]){
    			prime[num++] = i;
    			// 从i*i开始就行,因为 2*i, 3*i, ... , (i-1)*i 已经被 2,...,i-1给筛掉了 
    			for(ll j=1ll*i*i; j<=n; j+=i) // 1ll表示将1换成ll类型 
    				if(!visited[j]) visited[j] = 1;
    		}
    	}
    	for(int i=0; i<num; ++i) {
    		cout << prime[i] << " ";
    	}
     } 
     int main()
     {
     	int n = 19;
     	cal(n);
     	return 0;
     }
    

    筛法求素数就是先认定所有的数是素数(int visited[10000]={0}),再通过一些方法把合数踢掉((visited[k*j] = 1)),一般筛法的思想是:从2开始找,素数的倍数(1倍、2倍、3倍,,,)为合数,都筛掉(标记为访问过了)。

    注意在标记i的倍数只需从i*i开始,因为小于它的i的倍数势必已经被更小的数作为倍数筛掉。同时小心i*iint

    但是这个写法会让一些数被重复筛。如30,会被2、3、5作为倍数筛三遍,这样的访问太多余了。

    时间复杂度(O(nlogn))

    2、欧拉筛法(线性筛)

    #include<bits/stdc++.h>
    using namespace std;
    #define N 10000
    #define ll long long 
    int visited[N];
    int prime[N];
    int num=0;
     // 线性筛
    void Euler(int n)
    {
    	memset(visited, 0, sizeof(visited));
    	for(int i=2; i<=n; ++i){
    		if(!visited[i]) prime[num++] = i;
    		
    		for(int j=0; j<num && 1ll*i*prime[j]<=n; ++j){
    			visited[i*prime[j]] = 1;
    			if(i%prime[j]==0) break; // 这一句话降低了时间复杂度
    									 // 这个break保证了合数只被最小质约数访问到。 
    		}
    	}
    	for(int i=0; i<num; ++i) {
    		cout << prime[i] << " ";
    	}
     } 
     
     int main()
     {
     	int n = 19;
    	Euler(n);
     	return 0;
     }
    

    这个break保证了合数只被最小质约数访问到。
    比如40=2*20=4*10=5*8,只有i=20时,才会在prim[j]=2的时候被访问到。
    i=10时,在prim[j]=2时就已经被break了;同样的,i=8时,在prim[j]也已经break了。

    快速线性筛法的特点就是不会重复筛除一个合数。它的原理是

    前提是:一个合数(i=p_1*p_2*...*p_n), (p_i)都是素数(2<=i<=n), pi<=pj ( i<=j )

    p1是最小的系数。这样每一个合数就有一个确定的表示方法,不会重复。(像12=2*2*3)

    我们现在规定一个合数由两个数相乘得到,那么合数有两种:

    1. 素数*素数=合数
    2. 一个最小的素数*合数=合数

    筛除i时:

    • 如果遇到i为素数,那么一个大的素数 i 乘以不大于 i 的素数,这样筛除的数跟之前的是不会重复的

    • 如果遇到i为合数,我们只认为合数由一个最小的素数*合数得到,也不会重复(就像12=2*6而不是12=3*4)

    时间复杂度(O(n))


    ps:以上代码我自己敲的,文字是以下几篇博客中摘取的整合的,如有侵权请告知。
    参考链接:

    https://blog.csdn.net/danliwoo/article/details/51871740

    https://blog.csdn.net/sodacoco/article/details/81519269

    https://blog.csdn.net/nuanxin_520/article/details/41207145


    刚看到除了线性筛,还有杜教筛、min_25筛、洲阁筛...不禁感叹你他娘的算法真的博大精深啊,以后有空再慢慢研究吧,这里先挖个坑。

  • 相关阅读:
    Linux零碎知识
    Xshell连接不上Linux
    Python中获取当前时间 获取当前时间前几天的代码
    重新设置Linux文件共享密码..
    【Python】【解决】UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 1: ordinal not in range(128)
    应该怎么理解 app = Flask(__name__)
    刚才在windows下发现拖拽不了文件了
    Hadoop点滴-何时使用hadoop fs、hadoop dfs与hdfs dfs命令
    Hadoop点滴-Hadoop分布式文件系统
    Hadoop点滴-初识MapReduce(2)
  • 原文地址:https://www.cnblogs.com/VanHa0101/p/14383312.html
Copyright © 2011-2022 走看看