zoukankan      html  css  js  c++  java
  • P5 判断一个数是不是质数

          如果一个散列表的容量为N,则在再散列的过程中,一般设置新散列表的容量为2N后面的第一个素数。从而,需要判断一个数字是不是质数,因而,本文就重点介绍这个知识点。

          例如,令N=7,则再散列之后,新散列表的容量为17。

          本文给出四个判断一个自然数是否为质数的算法和算法的Java实现,并且分析其时间复杂度。

    基本概念

          质数(prime number)又称素数,是指在大于1的自然数中,除了1和它本身以外不再有其它因数的自然数。如果有其它因数,则称为合数。

          显然,1既不属于质数也不属于合数,最小的质数是2,最小的合数是4。记自然数为N,N的平方根为sqrt(N)。下面给出四个判断一个自然数是否为质数的思路。

    算法1 穷举法

          根据质数定义,判断一个整数N是否为素数,只需用从2 到 N-1 之间的每一个整数去除N,如果都不能被整除,那么 N就是一个素数。 

       /**
         * 判断n是否为素数
         * 
         * 不能被2~n-1间的整数整除则为素数,返回 true
         *
         * @param num
         * @return true 是素数
         */
        private static boolean isPrimeTraversal(int num) {
            System.out.println("方法名:isPrimeTraversal");
            if (num < 2) {
                return false;
            }
    
            int j;
            for (j = 2; j < num; j++) {
                if (num % j == 0) {
                    System.out.println(num + " is not a prime number");
                    return false;
                }
            }
            System.out.println(num + " is a prime number");
            return true;
        }

     算法2

          如果 N不能被 2 ~ sqrt(N) 之间任一整数整除,那么N必定是素数。

          分析:记x和y都是自然数,且x<=y;令N=x*y,则N<=(x^2+y^2)/2,当且仅当x=y时,等号成立。显然,x=y时,N=x^2。

          如果N是合数,则1<x<= sqrt(N) <=y。所以,只需要遍历不大于sqrt(N)的自然数即可。 

        /**
         * @param num
         * @return true is a prime number
         */
        private static boolean isPrime(int num) {
            if (num < 2) {
                return false;
            }
            // you can also use i <= p / 2
            int sqrt = 1 + (int) Math.sqrt(num);
            for (int i = 2; i < sqrt; i++) {
                // 若能被整除,则说明是合数,返回false
                if (num % i == 0) {
                    return false;
                }
            }
            return true;
        }

           算法2的时间复杂度为O(sqrt(N) ),小于算法1的O(N),效率更高。

    算法3 Eratosthenes筛选法

          公元前250年,古希腊著名数学家埃拉托塞尼(Eratosthenes)提出一种筛选法:要得到不大于某个自然数N的所有素数,只要在2至N中将不大于sqrt(N)的素数的倍数全部划去即可。

          上述方法等价于“如果N是合数,则它有一个因数d满足1<d≤sqrt(N)”。(《基础数论》13页,U杜德利著,上海科技出版社)。

          严格而言,这里并非判断一个数是否为素数,而是寻找小于N的所有素数,放在本文有点牵强附会。鉴于是一个不错的算法,姑且如此吧。

        /**
         * 筛选法查找区间[0, n) 所有素数
         * @param n
         */
        private static void screenPrimeNum(int n) {
            // 数组bs初始化时默认值为false
            boolean[] bs = new boolean[n];
    
            for (int i = 2; i < n; i++) {// 从2开始
                for (int j = i + 1; j < n; j++) { //从i+1循环就行
                    if (j % i == 0) {
                        // j是质数i的倍数,把数组元素bs[j]赋值为true
                        bs[j] = true;
                    }
                }
            }
            StringBuilder sb = new StringBuilder();
            // 0和1不是质数,因此从2开始循环
            for (int i = 2; i < n; i++) {
                // 元素为false的下标就是我们苦苦寻觅的素数
                if (!bs[i]) {
                    sb = sb.append(i).append(",");
                }
            }
            System.out.println(sb.substring(0, sb.length() - 1));
        }

           这个算法的时间复杂度是O(NloglogN),在此,数学证明过程就忽略了。如果用算法1和算法2查找小于N的所有素数,容易求得,应用算法2时的时间复杂度为O(N*sqrt(N) ),应用算法1时的是O(N*N)。

    算法4 基于六的求模算法

          质数有一个特点,除了2和3之外,它总可以表示为6 N±1的形式,其中 N是大于等于1的自然数。

          分析 任何一个自然数,总可以表示成为如下的形式之一:

      6N,6N+1,6N+2,6N+3,6N+4,6N+5 (N=0,1,2,…)

          显然,当N≥1时,6N,6N+2,6N+3,6N+4都不是素数,只有形如6N+1和6N+5的自然数有可能是素数。所以,除了2和3之外,所有的素数都可以表示成6N±1的形式(N为大于1的自然数)。故循环判断的时候,步长可以设置为6,然后判断6 N±1有无因数即可。

          对于输入的自然数较小时,也许效果不怎么明显,但是如果自然数越来越大,那么该方法的执行效率就会越来越明显,而且,要明显优于筛选法。

        /**
         * 以6为步长,校验一个数字是否为质数
         * @param num
         * @return true is a prime number
         */
        public static boolean isPrime6(int num) {
            if (num <= 3) {
                return num > 1;
            }
            // 不在6的倍数两侧的一定不是质数
            if (num % 6 != 1 && num % 6 != 5) {
                return false;
            }
            int sqrt = 1 + (int) Math.sqrt(num);
            //在6的倍数两侧的也可能不是质数
            for (int i = 5; i < sqrt; i += 6) {
                if (num % i == 0 || num % (i + 2) == 0) {
                    return false;
                }
            }
            return true;
        }

           算法的时间复杂度是O(sqrt(N) ),但是,有明显的步长优势,海量数据校验时,效果不可小觑,比如10w+。 

    求素数练习题

          下面给出两道练习题及其Java实现。

          一、 求两个整数之间的素数。

        /**
         * 找到两个自然数之间的素数
         */
        private static void findAllPrimeNumbers(int begin, int end) {
            StringBuilder sb = new StringBuilder();
            for (; begin <= end; begin++) {
                if (isPrime(begin)) {
                    sb = sb.append(begin).append(",");
                }
            }
            System.out.println(sb.substring(0, sb.length() - 1));
        }

           二、求一个数字乘以2的值之后的第一个素数。

        /**
         * 查询自然数的、2的倍数(2*num)之后的第一个素数
         * @param num
         *
         */
        private static void nextPrime(int num) {
            num = 2 * num;
            StringBuilder sb = new StringBuilder();
            while (true) {
                if (isPrime6(num)) {
                    break;
                }
                num ++;
            }
            System.out.println("自然数两倍之后的第一个素数是:" + num);
        }

    算法实现整合

          这里给出完整的Java代码实现。 

    package hello;
    public class IsPrime {
    
        public static void main(String[] args) {
    
            int num = 17;
            
            nextPrime(num);
            boolean bool = false;
            bool = isPrime(num);
            if (bool) {
                System.out.println(num + " is a prime number");
            } else {
                System.out.println(num + " is not a prime number");
            }
    
            bool = isPrime6(num);
            if (bool) {
                System.out.println(num + " is a prime number proofed by isPrime6");
            } else {
                System.out.println(num + " is not a prime number proofed by isPrime6");
            }
            System.out.println("应用实例——找到两个自然数之间的素数");
            int begin = 0, end = 101300;
            long beginT = System.currentTimeMillis();
            findAllPrimeNumbers(begin, end);
            long middle = System.currentTimeMillis();
            System.out.println(middle - beginT + "是耗时时长。下面使用筛选法找到小于这个整数的所有素数");
            screenPrimeNum(end);
            System.out.println("筛选法耗时是 " + (System.currentTimeMillis() - middle));
         }
    
        /**
         * @param num
         * @return true is a prime number
         */
        private static boolean isPrime(int num) {
            if (num < 2) {
                return false;
            }
            // you can also use i <= p / 2
            int sqrt = 1 + (int) Math.sqrt(num);
            for (int i = 2; i < sqrt; i++) {
                // 若能被整除,则说明是合数,返回false
                if (num % i == 0) {
                    return false;
                }
            }
            return true;
        }
    
        /**
         * 以6为步长,校验一个数字是否为质数
         * @param num
         * @return true is a prime number
         */
        public static boolean isPrime6(int num) {
            if (num <= 3) {
                return num > 1;
            }
            // 不在6的倍数两侧的一定不是质数
            if (num % 6 != 1 && num % 6 != 5) {
                return false;
            }
            int sqrt = 1 + (int) Math.sqrt(num);
            //在6的倍数两侧的也可能不是质数
            for (int i = 5; i < sqrt; i += 6) {
                if (num % i == 0 || num % (i + 2) == 0) {
                    return false;
                }
            }
            return true;
        }
    
        /**
         * 判断n是否为素数
         * <p>
         * 不能被2~n-1间的整数整除则为素数,返回 true
         *
         * @param num
         * @return true 是素数
         */
        private static boolean isPrimeTraversal(int num) {
            System.out.println("方法名:isPrimeTraversal");
            if (num < 2) {
                return false;
            }
    
            int j;
            for (j = 2; j < num; j++) {
                if (num % j == 0) {
                    System.out.println(num + " is not a prime number");
                    return false;
                }
            }
            System.out.println(num + " is a prime number");
            return true;
        }
    
        /**
         * 找到两个自然数之间的素数
         */
        private static void findAllPrimeNumbers(int begin, int end) {
            StringBuilder sb = new StringBuilder();
            for (; begin <= end; begin++) {
                if (isPrime6(begin)) {
                    sb = sb.append(begin).append(",");
                }
            }
            System.out.println(sb.substring(0, sb.length() - 1));
        }
    
        /**
         * 查询自然数的、2的倍数(2*num)之后的第一个素数
         * @param num
         *
         */
        private static void nextPrime(int num) {
            num = 2 * num;
            StringBuilder sb = new StringBuilder();
            while (true) {
                if (isPrime6(num)) {
                    break;
                }
                num ++;
            }
            System.out.println("自然数两倍之后的第一个素数是:" + num);
        }
    
        /**
         * 筛选法查找区间[0, n) 所有素数
         * @param n
         */
        private static void screenPrimeNum(int n) {
            // 数组bs初始化时默认值为false
            boolean[] bs = new boolean[n];
    
            for (int i = 2; i < n; i++) {// 从2开始
                for (int j = i + 1; j < n; j++) { //从i+1循环就行
                    if (j % i == 0) {
                        // j是质数i的倍数,把bs[j]赋值为true
                        bs[j] = true;
                    }
                }
            }
            StringBuilder sb = new StringBuilder();
            // 0和1不是质数,因此从2开始循环
            for (int i = 2; i < n; i++) {
                // 元素为false的下标就是我们苦苦寻觅的素数
                if (!bs[i]) {
                    sb = sb.append(i).append(",");
                }
            }
            System.out.println(sb.substring(0, sb.length() - 1));
        }
    
    }

     小 结

          首先给出四种求解素数的思路,并分析其执行性能;然后给出相应的Java实现;最后,展示一个求素数的实战训练找出两个自然数之间的素数。 

    Reference 

    https://www.cnblogs.com/sea-stream/p/12098804.html

    https://baike.baidu.com/item/%E8%B4%A8%E6%95%B0/263515

  • 相关阅读:
    0509操作系统
    0508数据结构
    计算机组成原理
    0510数据库--基础知识
    0508操作系统
    0507数据结构
    0506操作系统和数据结构
    机试题201702x--不定长数组的输入
    机试题201805--输入n个字符串,将其反转输出
    SSH框架--Hibernate配置
  • 原文地址:https://www.cnblogs.com/east7/p/12681483.html
Copyright © 2011-2022 走看看