Java算法——判断素数
public static boolean isPrimeNumber(int number){
if(number<2)
return false;
for(int i=2;i<=Math.sqrt(number);i++){
if(number%i==0&&number!=2)
return false;
}
return true;
}
素数算法(二)
上次讨论了简单的素数判定的算法,不过这个算法对于位数较大(一般小于108)的素数判定就显得相当力不从心了。比如在素数目前最广泛的应用领域-公共密钥体系中,一般选择的素数都是相当大的(通常在100位以上),如果采用上次的试除法来判定,那么可能要穷尽你一生的时间都还不够。所以在一般的应用领域,人们采用的是Rabin-Miller检验法。下面是该检验法的算法:
首先选择一个代测的随机数p,计算b,b是2整除p-1的次数。然后计算m,使得n=1+(2^b)m。
(1) 选择一个小于p的随机数a。
(2) 设j=0且z=a^m mod p
(3) 如果z=1或z=p-1,那麽p通过测试,可能使素数
(4) 如果j>0且z=1, 那麽p不是素数
(5) 设j=j+1。如果j<b且z<>p-1,设z=z^2 mod p,然后回到(4)。如果z=p-1,那麽p通过测试,可能为素数。
(6) 如果j=b 且z<>p-1,不是素数
数a被当成证据的概率为75%。这意味着当迭代次数为t时,它产生一个假的素数所花费的时间不超过1/4^t。实际上,对大多数随机数,几乎99.99%肯定a是证据。
实际考虑:
在实际算法,产生素数是很快的。
(1) 产生一个n-位的随机数p
(2) 设高位和低位为1(设高位是为了保证位数,设低位是为了保证位奇数)
(3) 检查以确保p不能被任何小素数整除:如3,5,7,11等等。有效的方法是测试小于2000的素数。使用字轮方法更快
(4) 对某随机数a运行Rabin-Miller检测,如果p通过,则另外产生一个随机数a,在测试。选取较小的a值,以保证速度。做5次 Rabin-Miller测试如果p在其中失败,从新产生p,再测试。
经测试,这个算法在sun的Sparc II工作站上实现:
2 .8秒产生一个256位的素数
24.0秒产生一个512位的素数
2分钟产生一个768位的素数
5.1分钟产生一个1024位的素数
最近在网上看了不少关于素数的问题,也学习到了不少东西,决定整理一下,算是一个学习的总结吧。
首先想说明的是,虽然素数可以进行很深入的研究(如在RSA公共密钥系统的应用),但是由于我对数论的不甚熟悉,所以只能做一些浅尝辄止的探讨,主要就是对一些简单的素数相关算法进行一个讨论。
首先来说说素数的判定算法,如果你是读谭浩强老师的《c程序设计》入门的话,那么一谈到素数的判定算法,你首先应该想到的就是以下的算法:给定一个正整数n,用2到sqrt(n)之间的所有整数去除n,如果可以整除,则n不是素数,如果不可以整除,则n就是素数。这个算法的时间复杂度十分明了,为O(sqrt(n)),算法的描述相当简单,实现也一样不困难。
# include <stdio.h>
# include <math.h>
int isPrime(int n)
{
int i ;
for(i=2; i <= sqrt(n); i++){
if(n%i == 0 )
break ;
}
if(i <= sqrt(n))
printf("%d is not a prime ! ", &n) ;
else
printf("%d is a prime ! ", &n) ;
return 0 ;
}
=====================================
public class SuShu{
private int num;
SuShu(int n){
num=n;
}
public boolean isSuShu(){
for(int i=2;i<num;i++){
if(num%i==0)
return false;
}
return true;
}
public static void main(String[] args){
for(int i=1;i<=100;i++){
SuShu su=new SuShu(i);
if(su.isSuShu())
System.out.println(i+"是素数");
else
System.out.println(i+"不是素数");
}
}
}
=============================
/**
* @param n
* @return if n is a prime return true, else false
*/
public static boolean isPrime(int n) {
// filte negative, zero, one
if (1 >= n) {
return false;
}
// 2 is a prime, stop filter
if (2 == n) {
return true;
}
// filter evens
if (0 == n % 2) {
return false;
}
// go on filting...
for (int a = 3; a <= Math.sqrt(n); a += 2) {
if (0 == n % a) {
return false;
}
}
// the rest is all prime, stop filting
return true;
}
==============================
//目前我认为最好的办法是:(不是lk的观点)
public boolean isPrime(int n){
for(int i = 2; i * i <= n; i++){
if(n % i == 0)
return false;
}
return true;
}
===============================
素数是这样的整数,它除了能表示为它自己和1的乘积以外,不能表示为任何其它两个整数的乘积。例如,15=3*5,所以15不是素数;又如,12=6*2=4*3,所以12也不是素数。另一方面,13除了等于13*1以外,不能表示为其它任何两个整数的乘积,所以13是一个素数。
有的数,如果单凭印象去捉摸,是无法确定它到底是不是素数的。有些数则可以马上说出它不是素数。一个数,不管它有多大,只要它的个位数是2、4、5、6、8或0,就不可能是素数。此外,一个数的各位数字之和要是可以被3整除的话,它也不可能是素数。但如果它的个位数是1、3、7或9,而且它的各位数字之和不能被3整除,那么,它就可能是素数(但也可能不是素数)。没有任何现成的公式可以告诉你一个数到底是不是素数。你只能试试看能不能将这
个数表示为两个比它小的数的乘积。
代码如下:
package com.vo;
public class Sushu {
public static void main(String[] args) {
int s=0;
int i;
for(i=0;i<=100;i++)
{
int j;
for(j=2;j<=i;j++){
if(i%j==0)
break;
}
if(i==j)
System.out.println(i);
}
}
}
----------------------------------------------------------------------------------
素数算法大全,及C程序实现优化详解 (一) 试除法
经常有初学者询问求解N内所有素数(质数)的问题,对此,网上的解答也很多,但很多要么不够专业,要么只有程序没有算法解析,所以三藏大厦对此问题做个小结,探讨一下求解素数的常见算法,同时给出相应的C语言程序及其解析。为了方便初学者理解,本文将从易到难阐述不同算法,高手可以直接看后面的高效算法
质数的定义
一个数,如果只有1和它本身两个因数,这样的数叫做质数,又称素数。
试除判断法
算法描述:从上述定义可知,素数不能被1和它本身之外的数整除,所以,判断一个数x是否素数只要看它是否能被2~sqrt(x)间的数整除即可;而求N内所有素数则是循环重复上述过程。
C语言实现:
复制内容到剪贴板
代码:
#include <time.h>
#include <malloc.h>
#define N 100000
// 简单试除判断法 Ver1
int s i m p l eDivisionV1(int n)
{
int i,j;
// 素数数量统计
int count = 0;
// 分配存放结果的空间
int* primes = (int*)malloc( sizeof(int)*n );
// 2是素数谁都知道,不算了
primes[count++] = 2;
// 循环计算3~n间的数
for (i=3; i<=n; i++)
{
// 为什么是sqrt(i),思考一下
for (j=2; j<=sqrt(i); j++)
{
// i被j整除,显然不是素数了
if (i%j == 0) break;
}
// i不能被2~sqrt(i)间的数整除,素数也
if (j > sqrt(i))
{
primes[count++] = i;
}
}
// 因输出费时,且和算法核心相关不大,故略
// 释放内存,别忘了传说中的内存泄漏
free(primes);
return count;
}
void main()
{
int count;
clock_t start, end;
// time函数不够精确,用clock凑合一下吧
start = clock();
count = s i m p l eDivisionV1(N);
end = clock();
printf("[%d]以内素数个数:%d, 计算用时:%d毫秒\n", N, count, end-start);
getch();
}
计算结果:
[100000]以内素数个数:9592, 计算用时:468毫秒
[1000000]以内素数个数:78498, 计算用时:10859毫秒
[5000000]以内素数个数:348513, 计算用时:103560毫秒
噢噢,算算十万还行,百万就10秒多了,而且时间增长很快,这不行,得优化一下!
优化分析:
仔细研究一下s i m p l eDivisionV1我们可以发现以下几个问题:
1. 在循环条件中重复调用sqrt(i)显然是比较浪费时间的
2. 判断素数,真的需要拿2~sqrt(i)间的所有整数去除吗?我们知道,合数都可以分解成若干质数,所以只要2~sqrt(i)间的质数不能整除i即可
根据上面两点,我们可将s i m p l eDivisionV1升级为s i m p l eDivisionV2,如下
复制内容到剪贴板
代码:
// 简单试除判断法 Ver2
int s i m p l eDivisionV2(int n)
{
int i, j, k, stop;
// 素数数量统计
int count = 0;
// 分配存放结果的空间
int* primes = (int*)malloc( sizeof(int)*n );
// 2是素数谁都知道,不算了
primes[count++] = 2;
stop = count;
// 循环计算3~n间的数
for (i=3; i<=n; i++)
{
k = sqrt(i);
// 在循环条件中重复调用sqrt是低效做法,故引入k
while (primes[stop] <= k && stop < count)
stop++;
// stop干什么用,思考一下
for (j=0; j<stop; j++)
{
if (i%primes[j] == 0) break;
}
// i不能被2~sqrt(i)间的素数整除,自然也不能被其他数整除,素数也
if (j == stop)
{
primes[count++] = i;
}
}
// 因输出费时,且和算法核心相关不大,故略
// 释放内存,别忘了传说中的内存泄漏
free(primes);
return count;
}
然后将main中调用的函数替换为s i m p l eDivisionV2,在看一下执行结果:
[100000]以内素数个数:9592, 计算用时:46毫秒
[1000000]以内素数个数:78498, 计算用时:546毫秒
[5000000]以内素数个数:348513, 计算用时:3515毫秒
[10000000]以内素数个数:664579, 计算用时:8000毫秒
很开心的看到,经过优化,速度提高了几十倍,尤其是时间增长曲线的坡度变小了,N值越大,V2函数比V1的效率就越高
对于试除判断这种质数算法来说,三藏认为s i m p l eDivisionV2基本已经接近极限,不大可能有量级上的突破了,有兴趣的朋友可以自己进一步优化。初学者除了参看上述例子外,可以尝试做各种修改及细节优化,也可以将除法变乘法,多加练习是学习编程的好方法。
虽然,上例中V2已经比V1快了很多了,但随着N的增大,耗时还是不少,那么我们还有更好的方法吗?