当前位置: 代码迷 >> J2SE >> 判断素数疑义
  详细解决方案

判断素数疑义

热度:296   发布时间:2016-04-24 00:41:45.0
判断素数疑问
给定一个整数x,判断x是否为素数。算法基本思路如下:让x被2到sqrt(x)除,如果x能被2至sqrt(x)之中任何一个整数整除,那么说明x不是质数,否则是质数。
我想问下问什么是到sqrt(x)呢?

------解决方案--------------------
你可以想象一下。开始是从2往大的除,当你除的数超过他的平方根时。结果会比平方根小。如果超过平方根了你还接着除。那相当于又从平方根倒着往2的方向除了一遍。明白吧?比如81的平方根是9,你除了2.3.4.5.6.7.8.9之后。如果再除10那结果会比9小。这个结果你在从2-9时已经除过。2*3=6你用6除3和除2结果是一样的道理,除过2了也就没必要再除3了。。。
------解决方案--------------------
sqrt(x) 是Math类里面的方法 你去JAVA文档找Math
------解决方案--------------------
探讨
你可以想象一下。开始是从2往大的除,当你除的数超过他的平方根时。结果会比平方根小。如果超过平方根了你还接着除。那相当于又从平方根倒着往2的方向除了一遍。明白吧?比如81的平方根是9,你除了2.3.4.5.6.7.8.9之后。如果再除10那结果会比9小。这个结果你在从2-9时已经除过。2*3=6你用6除3和除2结果是一样的道理,除过2了也就没必要再除3了。。。

------解决方案--------------------
就是从2开始+1累加到sqrt(x)....比sqrt(x)大的数如果能被所求的数整除,则他的整除结果肯定在2到sqrt(x)中的数里面....
------解决方案--------------------
测试某一个数是否为素数(合数)的 Miller-Rabin 算法,效率远比根号验证法快。

Java code
public class MillerRabin {    public static void main(String[] args) {        long t0, t1;        t0 = System.nanoTime();        boolean b = !isComposite(479001599);        boolean c = !isComposite(456789012);        t1 = System.nanoTime();        System.out.println(t1 - t0);        System.out.println(b + " " + c);    }    /**     * <p>Miller-Rabin 测试某一个数是否是合数</p>     *     * @param n 需要测试的数     * @return true: 该数为合数;false: 该数为素数     */    public static boolean isComposite(int n) {        if (n < 2) {            throw new IllegalArgumentException("number must greater than or equals 2");        }        // 排除 2、3、5、7 以加速测试        if (n == 2 || n == 3 || n == 5 || n == 7) {            return false;        }        // 偶数        if ((n & 1) == 0) {            return true;        }        // 排除 3、5、7 的倍数,以加速测试        if (n % 3 == 0) {            return true;        }        if (n % 5 == 0) {            return true;        }        if (n % 7 == 0) {            return true;        }        // 寻找 s 和 d 以满足 n = 2^s * d + 1        int s = 0, d = n - 1;        while ((d & 1) == 0) {            d >>= 1;            s++;        }        // 对于各种数值需要进行 Miller-Rabin 基准测试的素数值        // 参考:http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Deterministic_variants_of_the_test        // if n < 1,373,653, it is enough to test a = 2 and 3;        // if n < 9,080,191, it is enough to test a = 31 and 73;        // if n < 4,759,123,141, it is enough to test a = 2, 7, and 61;        // if n < 2,152,302,898,747, it is enough to test a = 2, 3, 5, 7, and 11;        // if n < 3,474,749,660,383, it is enough to test a = 2, 3, 5, 7, 11, and 13;        // if n < 341,550,071,728,321, it is enough to test a = 2, 3, 5, 7, 11, 13, and 17.        if (n < 1373653) {            if (loopMillerRabin(s, d, n, 2, 3)) {                return true;            }        } else if (n < 9080191) {            if (loopMillerRabin(s, d, n, 31, 73)) {                return true;            }        } else {            // 4,759,123,141 已经超过 int 的最大值,因此大于等于 9080191 就采用 4,759,123,141 的基准测试            if (loopMillerRabin(s, d, n, 2, 7, 61)) {                return true;            }        }        return false;    }    /**     * <p>循环 Miller-Rabin 测试</p>     *     * @param s n = 2^s * d + 1 中的 s 值     * @param d n = 2^s * d + 1 中的 d 值     * @param n 需要测试的数     * @param t 测试的基准素数     */    private static boolean loopMillerRabin(int s, int d, int n, int... t) {        for (int i = 0; i < t.length; i++) {            if (testMillerRabin(t[i], s, d, n)) {                return true;            }        }        return false;    }    /**     * <p>Miller-Rabin 基本测试</p>     *     * @param a 素性测试基准素数     * @param s n = 2^s * d + 1 中的 s 值     * @param d n = 2^s * d + 1 中的 d 值     * @param n 需要测试的数     * @return 测试某一数是否是合数。true: 该数是合数;false: 该数可能是素数。若返回 false     * 需要进行多基准的联合测试才能判断该数确实是素数     */    private static boolean testMillerRabin(int a, int s, int d, int n) {        if (montgomery(a, d, n) != 1) {            int e = 1;            for (int i = 0; i < s; i++) {                if (montgomery(a, d * e, n) + 1 == n) {                    return false;                }                e <<= 1;            }            return true;        }        return false;    }    /**     * <p>使用 Montgomery 算法计算 (base ^ exp) % mod 的值。</p>     *      * <p>由于 Java 中 int 的运算速度远远大于 long 的运算速度,因此该算法需要改进!</p>     *     * @param base 基数     * @param exp 指数     * @param mod 模数     */    private static int montgomery(int base, int exp, int mod) {        if (base > 46340 || mod > 46340) {            long temp = 1;            long prod = base % mod;            while (exp > 1) {                if ((exp & 1) != 0) {                    temp = (temp * prod) % mod;                }                prod = (prod * prod) % mod;                exp >>= 1;            }            return (int) ((temp * prod) % mod);        } else {            int temp = 1;            int prod = base % mod;            while (exp > 1) {                if ((exp & 1) != 0) {                    temp = (temp * prod) % mod;                }                prod = (prod * prod) % mod;                exp >>= 1;            }            return (temp * prod) % mod;        }    }    /**     * <p>     * 根据复化辛普生法则计算高斯 Li 函数。Li(x) 近似于素数个数函数 π(x)     * </p>     *      * @param x 数值范围     * @return 该值以内的素数个数的估计值     */    private static double gaussLi(int x) {        int n = x;        double s = 2;        double h = (x - s) / n;        double a = f(s + h / 2);        double b = 0;        for (int k = 1; k < n; k++) {            a += f(s + k * h + h / 2);            b += f(s + k * h);        }        return (h / 6) * (f(s) + 4 * a + 2 * b + f(x));    }    /**     * <p>     * Guass Li(x) 积分函数项     * </p>     *      * @param x     * @return     */    private static double f(double x) {        return 1 / Math.log(x);    }}
  相关解决方案