素数算法(小结)

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程序实现优化详解 () 试除法

发布日期: 2009-05-04   来源: Doforfun.net   作者:三藏法师

经常有初学者询问求解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的增大,耗时还是不少,那么我们还有更好的方法吗?

 

你可能感兴趣的:(素数算法(小结))