日期:2014-05-20  浏览次数:20978 次

判断素数疑问
给定一个整数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);
    }
}