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

    1. Eratosthenes筛法(埃氏筛)

    流程:对于每一个质数(p),标注出范围内所有(p)的倍数为合数,剩下的就是质数。

    int cnt, prm[MAX], v[MAX];
    
    void Eratosthenes()
    {
    	for (int i = 2; i <= N; i++)
    		if (!v[i]) {//如果未被标记,则是质数
    			prm[++cnt] = i;
    			for (int j = 2; i * j <= N; j++)
    				v[i * j] = 1;//标记所有i的倍数
    		}
    }
    

    复杂度为(O(nloglogn))

    感性理解:每个质数都要在(N)范围内筛出所有倍数,运算量为

    [frac N2 + frac N3 + frac N5 +.... = N(frac 12+frac 13 + frac 15+...) ]

    我们知道调和和(1+frac 12+ frac 13+...+ frac 1n)积分估值是趋近于(ln n)的。同时由质数定理,(N)很大时,质数的密度为(frac 1{lnn})。可以近似认为平均每(ln n)个数中有一个质数,计第(i)个质数为所以“质数调和级数”约为

    [1+frac 12+ frac 13+...+ frac 1{lnn} = lnlnn ]

    最后就出现了两个(log)

    证明:知乎上看到的转载,很复杂,关键在于证明(sum_{ple n} frac 1p)的值。这篇文章证明了Mertens' theorems,即质数调和级数与(lnlnn)差为定值。

    2. Eratosthenes的优化

    1. 对于合数(n=p_1p_2...p_k),显然至少有一个质因子是小于(sqrt n)的,于是只需要枚举到(sqrt n),可以保证之后没被标记的都是质数,合数均已被标记。
    2. 当枚举到质数(p)时,(p)以下的质数都已经枚举过了,所以(2p,3p,5p,...)已经被标记,直接从(p^2)开始标记即可。
    int cnt, prm[MAX], v[MAX];
    
    void Eratosthenes()
    {
    	for (int i = 2; i * i <= N; i++) // 1
    		if (!v[i]) {
    			prm[++cnt] = i;
    			for (int j = i; i * j <= N; j++) // 2
    				v[i * j] = 1;
    		}		
    }
    
    

    复杂度没有变化,但是降低了常数

    3. Euler筛法(欧拉筛)

    埃氏筛中每个合数会被所有质因子分别筛一次。欧拉筛巧妙的设计使得每个合数只会被筛一次。

    流程:从小到大枚举每一个数,将小于等于该数最小质因子的所有质数与该数相乘并标记。并记录每个数的最小质因子供下次标记。

    比如说当前枚举到(7 imes11 = 77),其最小质因子为7,那么将(2 imes77,3 imes77,5 imes77,7 imes77)标记即可。

    为什么这样是线性的?如果合数分解(n = p_1p_2...p_k(p_1le p_2 le...))由于规定了“小于最小质因子的质数才会相乘标记”,(n)只会被(p_1(p_2p_3...p_k))的组合标记一次,即每个合数都由最小质因子来标记。如上文中(2 imes77)只会由2标记一次,而(11*77) 没有被标记,他的最小质因子为7,会在稍后被(7 * 121)标记。这样就做到的合数与标记筛除的一一对应。复杂度为(O(n))

    void Euler()
    {
    	for (int i = 2; i <= N; i++) {
    		if (!v[i]) { //v[i]除去标记外,还记录每个数最小质因子的 
    			v[i] = i;
    			prm[++cnt] = i;
    		}
    		for (int j = 1; j <= cnt; j++) {
    			//枚举小于等于v[i]的所有质数并标记 
    			if (prm[j] > v[i] || prm[j] * i > N) break;
    			v[prm[j] * i] = i;//记录其最小质因子 
    		}
    	}
    }
    

    在实际应用中欧拉要更常用,不过(loglogn)实在太小,而且欧拉筛常数稍大,两者的效率差距很小。

  • 相关阅读:
    多线程 wait和sleep区别
    什么是分布式系统,如何学习分布式系统
    Mybatis传多个参数(三种解决方案)
    UML中类之间的几种关系
    前缀、中缀、后缀表达式
    数据库连接池c3p0和dbcp
    代码收藏
    spark教程(12)-生态与原理
    spark教程(10)-sparkSQL
    spark教程(九)-操作数据库
  • 原文地址:https://www.cnblogs.com/ofnoname/p/15098362.html
Copyright © 2011-2022 走看看