leetcode204-统计质数个数之一步步调试超时

题目:统计所有小于非负整数 n 的质数的数量

0 <= n <= 5000000

示例 1:输入:n = 10, 输出:4 , 解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 

示例 2:输入:n = 0 ,输出:0;输入:n = 1 ,输出:0

思路:n=0,1,2时,容易得到数量为0;n>2时,遍历[3,n)的所有整数,判断是否为质数

先提下数学基础:质数也叫素数,n(n>1)为素数当且仅当n只有1和n两个因子(就是约数)

另外,一个非负自然数的因子总是成对出现,因为是整除,总有除数和商,就是他俩

解答

第一次提交:

class Solution {
    public int countPrimes(int n) {
        if (n == 0 || n == 1 || n == 2) {
            return 0;
        }
        int count = 0;
        for (int i = 2; i < n; i++) {
            boolean isPrime = true;
            for (int j = 2; j < i; j++) {-----------------------范围缩小
                if (i % j == 0) {
                    isPrime = false;
                    break;
                } 
            }
            if (isPrime) {
                count++;
            }
        }
        return count;
    }
}

提交:超时...............这里很容易想到,在判断是否为素数时,其实不需要判断到i-1,只用判断到[2,i/2]即可

修改提交:

class Solution {
    public int countPrimes(int n) {
        if (n == 0 || n == 1 || n == 2) {
            return 0;
        }
        int count = 0;
        for (int i = 2; i < n; i++) {
            boolean isPrime = true;
            for (int j = 2; j <= i/2; j++) {-----------------------范围需要继续缩小
                if (i % j == 0) {
                    isPrime = false;
                    break;
                } 
            }
            if (isPrime) {
                count++;
            }
        }
        return count;
    }
}

提交--------->超时,提示是在n=499979时超时的;看起来还要优化,那就从数学角度,这次修改,j的上界是i/2,

这是因为从2开始遍历的,而2是最小的素数;一个自然数的约数总是成对出现,那在上面的提示位置,可以在判断

i不能整除j时,将上界继续缩小;可以定义一个变量high初始值为i/2,并随着j变化:high=i/j;

修改提交:

class Solution {
    public int countPrimes(int n) {
        if (n == 0 || n == 1 || n == 2) {
            return 0;
        }
        int count = 0;
        for (int i = 2; i < n; i++) {
            boolean isPrime = true;
            int high = i/2;
            for (int j = 2; j < i; j++) {-----------------------范围需要继续缩小
                if(j>high){
                    break;
                }               
                if (i % j == 0) {
                    isPrime = false;
                    break;
                } 
                high = i/j;
            }
            if (isPrime) {
                count++;
            }
        }
        return count;
    }
}    

提交-------------->呵呵呵,还是超时,提示是在n=1500000,优化起效了,于是继续探索

如果我判断了不能被2整除,还需要判断能不能被4,8,16...这些数整除吗?不需要

如果我判断了不能被3整除,还需要判断能不能被6,9,12...这些数整除吗?不需要

如果我判断了不能被7整除,还需要判断能不能被14,35,49...这些数整除吗?也不需要

我们已知所有合数均能分解为一个个质因子,所以,对一个自然数n,只需要遍历所有小于n的质数,如果n都不能整除它们,

那n就是素数,正好第一层的循环可以保存所有上一次判断出来的质数,为了一步到位,前面探索high的定义结合在一起

修改提交:

public class Solution {

    public static int countPrimes(int n) {
        if (n == 0 || n == 1 || n == 2) {
            return 0;
        }
        int count = 0;
        List<Integer> list = new ArrayList<>();
        for (int i = 2; i < n; i++) {
            boolean isPrime = true;
            int high = i / 2;
            for (int j = 0; j < list.size(); j++) {
                int a = list.get(j);
                if (a > high) {
                    break;
                }
                if (i % a == 0) {
                    isPrime = false;
                    break;
                }
                high = i / a;
            }
            if (isPrime) {
                count++;
                list.add(i);
            }
        }
        return count;
    }
}
通过,超过25%的java提交者
执行用时: 341 ms
内存消耗: 38.6 MB
常规思路,只打败了25%......优化余地还很大,推测是求余运算和除法运算耗时间,或者是判断素数的方式还有更优的
这里就不单独去求证素数的特殊性质了
原文地址:https://www.cnblogs.com/yb38156/p/14645495.html