素数

 helloworld之后,就该写点真正有意义的程序了。
经常有人会问,为什么他写的素数程序看上去挺完美,但是跑出来的结果不对?
通常,2重循环的内循环,满足某种条件后该内循环终止,外循环加一的情况,需要加个标志(flg),以标明内循环的终止,是因为内循环正常结束了?还是满足条件了?
一定范围的素数寻找,就是这样的。跑出来的结果不对,通常就是标志(flg)的初始值设定,判定逻辑不对,甚至,标志(flg)根本就没有。
具体的错误程序就不再谈了,素数的定义,相关定理网上到处有,就不c&p了,但是还是应该好好了解一下,以后研究密码学的时候,还是非常有用的。

开始用java写个素数检验程序吧。

1, 最朴素的算法:
由2开始到n-1,一个一个去判断是否可以整除,如果一旦满足,就是非素数,判断终止。
    public static boolean isPrime(long num) {
        for (int j = 2; j < num; j++) {
            if (num % j == 0) {
                return false;
            }
        }
        return true;
    }

2,一定范围内的素数:
用上面的isPrime方法,最朴素的输出一定范围的素数。
        long longTime = System.currentTimeMillis();
        for (long i = 3; i <= 10000; i = i + 2) {
            if (isPrime(i))
                System.out.print(i + " ");
        }
        System.out.println();
        System.out.println((System.currentTimeMillis()-longTime)+"ms");
执行时间:328ms

3,素数判断算法的优化:
很多人(包括俺自己)一开始就发现朴素的素数判断算法的效率问题,就是不需要判断到n-1。

那么,判断到多少就足够了呢?很多人(包括俺自己)以为到n/2就可以了,很可惜n/2还是太大了,没有必要。实际上,只要循环到n^(1/2)就可以了。具体的证明,用反证法非常简单,就不叙述了。
唯一的,看到有的文章说要循环到,n^(1/2)+1或者(n+1)^(1/2),这是没有必要的,就循环到n^(1/2)向下取整就可以。

    public static boolean isPrime(long num) {
        long sqrtI = (long) Math.sqrt(num);
        for (int j = 2; j <=sqrtI; j++) {
            if (num % j == 0) {
                return false;
            }
        }
        return true;
    }

执行时间:110ms

4,改进的一定范围内的素数:
3,5,7,9...递增2的去判断,还不是够好的办法,可以做如下的改进:
考察6m:
6m+0(2,3的倍数,合数)
6m+1(要判断,可能是素数)
6m+2(2的倍数,合数)
6m+3(3的倍数,合数)
6m+4(2的倍数,合数)
6m+5(要判断,可能是素数)
原来需要判断1/2的数,现在只要判断1/3的,提高了些。程序如下:

        long longTime = System.currentTimeMillis();
        for (long i = 1; i <= 1667; i++ ) {
            if (isPrime(6*i+1))
                System.out.print((6*i+1) + " ");
            if (isPrime(6*i+5))
                System.out.print((6*i+5) + " ");
        }
        System.out.println();
        System.out.println((System.currentTimeMillis() - longTime) + "ms");

执行时间:94ms

当然考察6(2*3)可以提到一些效率,那么考察30(2*3*5),只要看 1,7,11 ,13,17,19,23,29(2,3,5意外的素数(1是例外)),8/30的数需要判断,又快了点......再快一点就是埃拉托斯特尼筛法了。

5 埃拉托斯特尼筛法:
根据改进的输出一定范围内的素数的算法,可以很容易发现,只要一个素数表,把这个范围内的素数的倍数筛选出去,剩下的就是素数了。那么素数表要多大呢? 2,3,5...n^(1/2)向上取整,就可以了。
反过来,有2,3,5...n的素数表,也可以得出2...n^2的素数表。

程序如下:
    public static int[] getPrimeNumbers(int num) {
        int[] primeNumbers = new int[num];
        for (int i = 0; i < num; i++)
            primeNumbers[i] = i + 1;
        primeNumbers[0] = 0;
        int i = 0;
        int j = num - 1;
        while (true) {
            for (; i < num; i++)
                if (primeNumbers[i] != 0)
                    break;
            for (; j >= 0; j--)
                if (primeNumbers[j] != 0)
                    break;
            if (primeNumbers[i] * primeNumbers[i] > primeNumbers[j]) {
                int m = 0;
                for (int n = 0; n < num; n++) {
                    if (primeNumbers[n] != 0)
                        m++;
                }
                int[] primeNumbersNew = new int[m];

                for (int n = 0, l = 0; n < num; n++) {
                    if (primeNumbers[n] != 0)
                        primeNumbersNew[l++] = primeNumbers[n];
                }
                return primeNumbersNew;
            }
            int prime = primeNumbers[i];
            for (int k = prime + prime; k <= j + 1; k += prime)
                primeNumbers[k - 1] = 0;
            i++;
        }
    }

输出程序如下:
        longTime = System.currentTimeMillis();
        int[] ints = getPrimeNumbers(10000);
        for (int i = 0; i < ints.length; i++)
            System.out.println(ints[i]);
        System.out.println((System.currentTimeMillis() - longTime) + "ms");

执行时间:32ms

在范围大的情况下,埃拉托斯特尼筛法的优势是明显的,因为它是n^(1/2)收敛的。
试一下1000000,方法4用了6047ms;而,埃拉托斯特尼筛法用了3625ms。

6 其他方法
以上的都是”试除法“,还有ECPP(使用椭圆曲线演算法,O((log n)^6))、AKS(多项式演算法,O((log n)^12))、Lucas-Lehemer(只用于测试梅森素数,2^(n-2)*log(2+3^(1/2)) )等方法,是利用数论中的一些定律,精确判定是否是素数的方法。具体的算法和程序,以后有时间,再写了。
除此以外,实践中概率式判断法也很常用,在一些非高机密的领域,比如银行军事意外的场合,用来生成密钥还是可行的。

 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值