zoukankan      html  css  js  c++  java
  • 筛素数以及判断数是否是素数

    判断一个数字是否是素数:

    解法1: O(n)

    public static boolean isPrime(int n){
        if (n <= 3) {
            return n > 1;
        }
        for(int i = 2; i < n; i++){
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }

    解法2: 假如n是合数,必然存在非1的两个约数p1和p2,其中p1<=sqrt(n),p2>=sqrt(n)。由此我们可以改进上述方法优化循环次数。如下:

    public static boolean isPrime(int n) {
        if (n <= 3) {
            return n > 1;
        }
        int sqrt = (int)Math.sqrt(n);
        for (int i = 2; i <= sqrt; i++) {
            if(n % i == 0) {
                return false;
            }
        }
        return true;
    }

    解法3:

    我们继续分析,其实质数还有一个特点,就是它总是等于 6x-1 或者 6x+1,其中 x 是大于等于1的自然数。

        如何论证这个结论呢,其实不难。首先 6x 肯定不是质数,因为它能被 6 整除;其次 6x+2 肯定也不是质数,因为它还能被2整除;依次类推,6x+3 肯定能被 3 整除;6x+4 肯定能被 2 整除。那么,就只有 6x+1 和 6x+5 (即等同于6x-1) 可能是质数了。所以循环的步长可以设为 6,然后每次只判断 6 两侧的数即可。
     对于输入的自然数 n 较小时,也许效果不怎么明显,但是当 n 越来越大后,该方法的执行效率就会越来越明显了。

    public static boolean isPrime(int num) {
        if (num <= 3) {
            return num > 1;
        }
        // 不在6的倍数两侧的一定不是质数
        if (num % 6 != 1 && num % 6 != 5) {
            return false;
        }
        int sqrt = (int) Math.sqrt(num);
        for (int i = 5; i <= sqrt; i += 6) {
            if (num % i == 0 || num % (i + 2) == 0) {
                return false;
            }
        }
        return true;
    }

    筛素数:

    一、一般筛素数(埃拉托斯特尼筛法)

    此筛选法的时间复杂度是O(nloglogn)

    int vis[maxn];
    memset(vis,1,sizeof(vis));
    void isprime()
    {
        int prime[maxn],cnt=0;
        for(int i=2;i<=sqrt(N);i++)
        {
            if(vis[i])
            {
                prime[cnt++]=i;
                for(int j=i*i;j<=N;j+=i)
                    vis[j]=0;//i是素数,则下一个起点是i*i,把后面的所有的i*i+2*n*i筛掉
            }
        }
    }

    这种方法比较好理解,初始时,假设全部都是素数,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数(注意上面的 i*i ,  比 i*2 要快点 ),把这些合数都筛掉。

    但仔细分析能发现,这种方法会造成重复筛除合数,影响效率。比如10,在i=2的时候,k=2*15筛了一次;在i=5,k=5*6 的时候又筛了一次。所以,也就有了快速线性筛法。

    二、线性筛素数(欧拉筛法)
    线性筛,复杂度为O(n)。与埃氏筛相比,不会对已经被标记过的合数再进行重复标记,故效率更高。欧拉筛将合数分解为 (最小质因数 * 一个合数) 的形式,通过最小质因数来判断当前合数是否已经被标记过。

    int vis[maxn];
    memset(vis,0,sizeof(vis));
    void isprime()
    {
        int prime[maxn],cnt=0;//cnt用来计数,prime数组保存素数 
        for(int i=2;i<=n;i++)
        {
            if(!vis[i])prime[cnt++]=i;//如果未被标记过,则表示为素数 
            for(int j=0;j<cnt && i*prime[j]<=n;j++)//当标记的合数超出范围则退出 
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]==0)break;//关键步骤 
            }
        }
    }

    难点就在于对if (i % prime[j] == 0)这步的理解,意思是每个数保证被它的最小质因数筛出,即当i是prime[j]的整数倍时,记 m = i / prime[j],那么 i * prime[j+1] 就可以变为 (m * prime[j+1]) * prime[j],这说明 i * prime[j+1] 是 prime[j] 的整数倍,不需要再进行标记(在之后会被 prime[j] * 某个数 标记),对于 prime[j+2] 及之后的素数同理,直接跳出循环,这样就避免了重复标记。
    参考文献:

    1 https://blog.csdn.net/qq_39826163/article/details/81395306

    2 https://blog.csdn.net/afei__/article/details/80638460

  • 相关阅读:
    Python解释器【转载】
    Python第一行代码
    Hive安装部署
    Python 3.6安装教程
    Spark安装部署
    Code:Blocks中文输出乱码解决方法
    HBase集群安装部署
    Hadoop集群时间同步
    ZooKeeper安装部署
    Linux重置mysql密码
  • 原文地址:https://www.cnblogs.com/xianbin7/p/10710802.html
Copyright © 2011-2022 走看看