• 素数判定与筛法


    本文作者MiserWeyte

    素数判定相关。包括筛法,也含更基础的算法及其常数优化。Pollard-Rho一类的就别想了。不会。
    本文是我第一次使用 (LaTeX) ,以(Markdown)编辑。


    一、朴素算法

    根据定义,枚举判断正整数 (n) 是否有除(1)和它本身以外的因数即可。时间复杂度 (O(n)) 。大数据下时间过长是显然的。

    bool is_prime(int n){
        if(n == 1) return false;
        if(n == 2) return true;
        for(int i=2; i<n; i++)
            if(n % i == 0) return false;
        return true;
    }
    

    二、一个小但有用的优化

    由于(n)的因数为成对出现的,我们只枚举了其中一个,枚举范围到(sqrt{n})即可。

    bool is_prime(int n){
        if(n == 1) return false;
        if(n == 2) return true;
        for(int i=2; i*i<=n; i++)
            if(n % i == 0) return false;
        return true;
    }
    

    其实,这种优化后时间复杂度为(O(sqrt{n})),已经可以满足大多数使用场合了。

    三、又一个常数优化

    这里要利用到一个定理:所有大于(3)的质数取余(6),得数均为(1)(5)。即,所有大于(3)的质数均为(6k+1)(6k-1)
    反证法证明如下:

    取余结果只可能为(0) ~ (5)。若为(0)(2)(4),则为偶数,不可能为质数。若为(3),则可以被(3)整除,不可能为质数。

    因此,对于取余结果不为(1)(5)的数,可以(O(1))得出该数不为质数。否则枚举(6kpm1)即可。

    bool is_prime(int n)
    {
        if(n == 1) return false;
        if(n == 2 || n == 3) return true;
        if(n % 6 != 1 && n % 6 != 5) return false;
        for(int i=5; i*i<=n; i+=6)
            if(n % i == 0 || n % (i + 2) == 0) return false;
        return true;
    }
    

    理论上最坏效率是上一个小优化后的三倍,即,时间复杂度(O(sqrt{n} / 3))

    四、本文主要内容:筛法

    筛法用于预处理出一定区间内的质数,便于使用时(O(1))查询。主要的我现在会的方法有一般筛法(埃拉托斯特尼筛法)与欧拉筛。

    埃拉托斯特尼筛法:先将所有数标为质数,每次选取最前面的质数,将其所有倍数标为合数。
    gif演示见下(来自维基百科):
    埃筛

    但是,由于有的数会被重复计算(如(6)(2)(3)均标记一次),达不到线性。
    时间复杂度(O(n log log n))(我并不会算)

    bool prime[MAXN];
    void make_prime() {
    	memset(prime, true, sizeof(prime));
    	prime[0] = prime[1] = false;
    	int sq = sqrt(MAXN);
    	for(int i=2; i<=t; i++) {
    		if(prime[i]) {
    			for(int j=2*i; j<MAXN; j+=i) {
    				prime[j]=false;
    			}
    		}
    	}
    }
    

    由于以上弊端,经过改良后的筛法——欧拉筛,可以达到保证每个合数只在最小质因子时被筛出。

    bool prime[MAXN];
    int Prime[MAXN];
    int num=0;
    void make_prime(){
    	memset(prime, true, sizeof(prime));
    	prime[0] = prime[1] = false;
    	for(int i=2; i<=MAXN; i++) {
    		if(prime[i]) {
    			Prime[num++]=i;
    		}
    		for(int j=0; j<num && i*Prime[j]<MAXN; j++) {
    			prime[i * Prime[j]]=false;
    			if(!(i % Prime[j]))
    				break;
    		}
    	}
    }
    

    该筛法的时间复杂度可以达到(O(n)),基本满足各种需求。

    注意,筛法不能用于数较大但请求较少的情况,否则会付出较大的时间和空间。

    五、就这么一提其他算法

    (Miller–Rabin primality test):更高效,虽然是一种不确定的质数判断法,但是在选择多种底数的情况下,正确率是可以接受的。
    (Pollard Rho)是一个非常玄学的方式,用于在(O(n^{1/4}))的期望时间复杂度内计算合数n的某个非平凡因子。事实上算法导论给出的是(O(sqrt p))(p)(n)的某个最小因子,满足(p)(n/p)互质。但是这些都是期望,未必符合实际。但事实上(Pollard~Rho)算法在实际环境中运行的相当不错。

    (the Meissel,Lehmer,Lagarias,Miller,Odlyzko Method):我也不知道它是啥,传说中的(O(n^{3/4}/log n))

  • 相关阅读:
    SQL Server中使用convert进行日期转换
    杂记
    sqlserver表分区与调优与行列转换
    HttpModule的认识与深入理解及MVC运行机制
    再谈委托
    ASP.NET forms凭据设置和跳转的几种方法
    IOS学习网址
    Activator.CreateInstance 方法 (Type) 的用法
    update多表更新的2种方式
    SQL自定义函数split分隔字符串
  • 原文地址:https://www.cnblogs.com/miserweyte/p/11628107.html
Copyright © 2020-2023  润新知