寻找下一个质数

Martyr2项目实现——Number部分的问题求解(4)Next Prime Number

问题描述:

Have the program find prime numbers until the user chooses to stop asking for the next one

翻译:

给定一个程序,在用户中断程序之前尽可能的寻找素数(可以转化为寻找前N个 素数)

原理:

常用的寻找素数的方法有两种,试除法和筛法

试除法:

​ 判断一个数是否为素数的基本方法就是试除法。

​ 将一个整数n除以每个大于2且小于等于\(\sqrt{n}\)的整数m(可以只使用素数m来检查). 如果 出现可以整除的整数,就说明整数n不是素数;否则所有的整数m都无法被n整除,说明整数 n为素数。

​ 试除法的一个问题是当测试的数目很大时,判断速度会很慢。

素数筛法:

​ 常用的素数筛法是埃拉托斯特尼筛法,可以用于找出一定范围内所有的素数。

​ 算法原理:

​ 从最小的素数2开始,将每个素数的倍数(这些数构成公差为该素数本身的等差数列)都标记为 合数。

​ 对于给定的整数范围n,找出\(\sqrt{n}\)以内的所有素数,并且用这些素数筛除对应的合数

​ 假定\(\sqrt{n}\)以内的素数为\(p_1,p_2,p_3,...p_k,\)对于素数\(p_i\),将它的倍数\(2p_i,3p_i,4p_i,...\)(<=N)都标记为合数,

​ 该筛法的时间复杂度为\(O(n\ln\ln{n})\)

​ 参考文献:埃拉托斯特尼筛法

程序实现:

  1. 试除法:

    public static ArrayList<Integer> MakePrime(int N){
        int sqrt_i;
        ArrayList<Integer> prime_list = new ArrayList<>();
        for(int i=2;i<=N;i++){
            sqrt_i = (int)Math.sqrt(i);
            //首先使用已生成的素数表进行试除
            if(!prime_list.isEmpty()) {
                int prime_num = prime_list.size();
                for (int j=0;j<prime_num;j++) {
                    int prime = prime_list.get(j);
                    if (prime <= sqrt_i) {
                        if (i % prime == 0)//如果i能被整除,就不是素数,直接跳出当前循环
                            break;
                    }else{
                        prime_list.add(i);
                        break;
                    }
                }
    
                int last_prime = prime_list.get(prime_list.size()-1);
                //如果当前生成的最大素数小于sqrt(i),检查完当前素数表,需要继续试除后续整数
                if(i%last_prime!=0&&last_prime<sqrt_i) {
                    int j = last_prime + 1;
                    while (j <= sqrt_i && i % j != 0) j++;
                    if (j > sqrt_i) prime_list.add(i);
                }
            }else{
                prime_list.add(i); //素数表为空,直接将素数2加入
            }
        }
        return prime_list;
    }
    
  2. 筛法:

    public static ArrayList<Integer> MakePrime2(int N){
        boolean[] isPrimes = new boolean[N+1];
        int sqrt_N = (int)Math.sqrt(N);
        for(int i=0;i<isPrimes.length;i++){
            isPrimes[i] = true;
        }
        for(int i=2;i<=sqrt_N;i++){
            if(isPrimes[i]) {//如果i是一个素数,就将i的倍数置为False(合数)
                //将素数对应的倍数置为false
                for (int j = i * 2; j <= N; j += i) {
                    isPrimes[j] = false;
                }
            }
        }
        ArrayList<Integer> prime_list = new ArrayList<>();
        for(int i=2;i<=N;i++)
            if(isPrimes[i]) prime_list.add(i);
        return prime_list;
    }
    

运行时间

我们通过计算1亿以内的全部素数,来比较试除法和筛法的运行时间,明显筛法速度快于试除法,但是筛法需要的空间要大于试除法,筛法需要用一个容纳整个搜索范围的数组空间,而试除法只需要一个保存全部素数的数组空间

前100000000个整数中素数的个数:5761455
使用试除法求取素数表的程序运行时间:23933ms
前100000000个整数中素数的个数:5761455
使用筛法求取素数表的程序运行时间:1748ms

posted @ 2020-10-20 10:45  sunfulv  阅读(873)  评论(0)    收藏  举报