求解素数几种方法

转贴文章请注明:逸学堂

 

求解一个算法,我们首先要知道它的数学含义.依据这个原则,首先我们要知道什么是素数.; 素数是这样的整数,它除了表示为它自己和1的乘积以外,无论他表示为任何两个整数的乘积。

找素数的方法多种多样。

1:是从2开始用“是则留下,不是则去掉”的方法把所有的数列出来(一直列到你不想再往下列为止,比方说,一直列到10,000)。第一个数是2,它是一个素数,所以应当把它留下来,然后继续往下数,每隔一个数删去一个数,这样就能把所有能被2整除、因而不是素数的数都去掉。在留下的最小的数当中,排在2后面的是3,这是第二个素数,因此应该把它留下,然后从它开始往后数,每隔两个数删去一个,这样就能把所有能被3整除的数全都去掉。下一个未去掉的数是5,然后往后每隔4个数删去一个,以除去所有能被5整除的数。再下一个数是7,往后每隔6个数删去一个;再下一个数是11,往后每隔10个数删一个;再下一个是13,往后每隔12个数删一个。就这样依法做下去。

但是编程我们一般不采用上面的方法,并不说这中方法计算机实现不了,或者说实现算法比较复杂。因为它更像一个数学推理。最后我们也给一个算法。

下面我们介绍几种长用的编程方法。

       2:遍历2以上N的平方根以下的每一个整数,是不是能整除N;(这是最基本的方法)

       3:遍历2以上N的平方根以下的每一个素数,是不是能整除N;(这个方法是上面方法的改进,但要求N平方根以下的素数已全部知道)
       4:采用Rabin-Miller算法进行验算;

例如:N=2^127-1是一个38位数,要验证它是否为素数,用上面几个不同的方法:

验算结果,假设计算机能每秒钟计算1亿次除法,那么
算法2要用4136年,算法3要用93年,算法4只要不到1秒钟!(这些数据是通过计算得到)

 

另外印度有人宣称素数测试是P问题,我一直没有找到那篇论文,听说里面有很多数学理论。如果那位大人有这篇论文,麻烦转发一份(E-Mail:exuetang@126.com)有任何问题也可以写信与我讨论。

 

下面我们分别实现上面的三种算法:

以下算法我们不涉及内存溢出,以及大数字的问题。如果测试数字超过2^32,发生内存溢出,你需要自己使用策略解决这个问题,在这里只讨论32位机有效数字算法。

1:// 算法0:是从2开始用“是则留下,不是则去掉”的方法把所有的数列出来

// 最后数组中不为0的数字就是要查找的素数。

void PrimeNumber0()

{

     //   int time ::GetTickCount();

     //   cout << "start time:" << time << endl;

 

     int Max[MAX_NUMBER];        // 在栈上分配,栈上空间要求一般都在2M之间,

     //   如果你需要更大空间,请在堆上申请空间(就是通过malloc,new来申请).

     memset(Max,0,MAX_NUMBER);

     for(int i = 0 ; i < MAX_NUMBER; ++i)

     {

          Max[i] = i;

     }

     int cout = 0;// 记录当前i的位置

 

     // 遍历整个数组

     for(i = 1; i < MAX_NUMBER; ++i)

     {

         if(Max[i] != 0 )// 如果数据不为0,说明是一个素数

         {

              int iCout = i;

              int j = Max[i];// 记录数组中数组位的数字,以便设置

              while((iCout+=j) < MAX_NUMBER)

              {

                   // 把不是素数的数位在数组中置为0

                   Max[iCout] = 0;

              }

              ++cout;

         }

     }

 

     //   int time ::GetTickCount();

     //   cout << "end time:" << time << endl;

}

 

2:这个算法可以修改成为,验证一个给定数字是否是一个素数。

// 因为我们讨论多个算法,所以我们把每个算法都单独

// 写在一个或多个函数内。这些函数并不要求输入值和返回值

// 如果你需要这些结果,可以自己修改。

 

// 算法1:遍历2以上N的平方根以下的每一个整数,是不是能整除N;

void PrimeNumber1()

{

//   int time ::GetTickCount();

//   cout << "start time:" << time << endl;

 

     int Max[MAX_NUMBER/2];      // 在栈上分配,栈上空间要求一般都在2M之间,

//   如果你需要更大空间,请在堆上申请空间(就是通过malloc,new来申请).素数的个数很少

// 所以没有必要申请和所求数字同样大小的空间。

     memset(Max,0,MAX_NUMBER);

     Max[0] = 2;// 放入第一个素数,有人说2不是素数,如果你是其中一员,就改成3吧

     int cout = 1;// 记录素数个数

 

     // 挨个数进行验证

     bool bflag = true;

     for(int i = 3; i < MAX_NUMBER; ++i)

     {

         bflag = true;

         // 需要是使用数学库(math.h)中sqrt

         int iTemp = (int)sqrt((float)i);// 强制转换成int类型,有的人在这里使用i+1就是为了增加sqrt的精度

         // 没有特殊函数,你也可以使用int iTemp = (int)sqrt(i)+1;来提高进度

         for (int j = 2; j < iTemp; ++j)

         {

              if(i%j == 0)// 求余,如果为0说明,可以整除,不是素数。

              {

                   bflag = false;

                   break;

              }

         }

         // 经过验证是素数,放入数组。

         if(bflag)

         {

              Max[cout++] = i;

         }

     }

 

//   int time ::GetTickCount();

//   cout << "end time:" << time << endl;

 

}

 

3:这个方法是上面方法的改进,但要求N平方根以下的素数已全部知道

// 算法2:遍历2以上N的平方根以下的每一个素数,是不是能整除N;

// (这个方法是上面方法的改进,但要求N平方根以下的素数已全部知道)

void PrimeNumber2()

{

     //   int time ::GetTickCount();

     //   cout << "start time:" << time << endl;

 

     int Max[MAX_NUMBER/2];      // 在栈上分配,栈上空间要求一般都在2M之间,

     //   如果你需要更大空间,请在堆上申请空间(就是通过malloc,new来申请).素数的个数很少

     // 所以没有必要申请和所求数字同样大小的空间。

     memset(Max,0,MAX_NUMBER);

     Max[0] = 2;// 放入第一个素数,有人说2不是素数,如果你是其中一员,就改成3吧

     int cout = 1;// 记录素数个数

 

     // 挨个数进行验证

     bool bflag = true;

     for(int i = 3; i < MAX_NUMBER; ++i)

     {

         bflag = true;

         // 需要是使用数学库(math.h)中sqrt

         int iTemp = (int)sqrt((float)i);// 强制转换成int类型,有的人在这里使用i+1就是为了增加sqrt的精度

         // 没有特殊函数,你也可以使用int iTemp = (int)sqrt(i)+1;来提高进度

/////////////////////////////////////////////////////////////////////////////////

         // 修改的是这里以下的部分

         for (int j = 0; j < cout; ++j)

         {

              if(i%Max[j] == 0)// 求余,如果为0说明,可以整除,不是素数。

              {

                   bflag = false;

                   break;

              }

         }

         // 修改的是这里以上的部分

//////////////////////////////////////////////////////////////////////////////////

         // 经过验证是素数,放入数组。

         if(bflag)

         {

              Max[cout++] = i;

         }

     }

 

     //   int time ::GetTickCount();

     //   cout << "end time:" << time << endl;

 

}

 

4:采用Rabin-Miller算法进行验算,Rabin-Miller算法是典型的验证一个数字是否为素数的方法。判断素数的方法是Rabin-Miller概率测试,那么他具体的流程是什么呢。假设我们要判断n是不是素数,首先我们必须保证n 是个奇数,那么我们就可以把n 表示为 n = (2^r)*s+1,注意s 也必须是一个奇数。然后我们就要选择一个随机的整数a (1<=a<=n-1),接下来我们就是要判断 a^s=1 (mod n) 或a^((2^j)*s)= -1(mod n)(0<=j<R),如果任意一式成立,我们就说n通过了测试,但是有可能不是素数也能通过测试。所以我们通常要做多次这样的测试,以确保我们得到的是一个素数。(DDS的标准是要经过50次测试)

 

// 算法3:采用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^s=1 (mod n) 或a^((2^j)*s)= -1(mod n)(0<=j<R),

 

bool Witness(int a,int n)

{

     // 解释一下数学词汇:

     // ceil求不小于x的最小整数,函数原型extern float ceil(float x);求得i的最大值

     // log计算x的自然对数,函数原型extern float log(float x);

     long i,d=1,x;

     for (i=(int)ceil(log((double)n-1)/log(2.0))-1;i>=0;--i)

     {

         x=d;

         d=(d*d)%n;

         if ((1==d) && (x!=1) && (x!=n-1))

         {

              return 1;

         }

         if ((n-1)&(1<0))

         {

              d=(d*a)%n;

         }

     }

     return (d!=1);

 

}

 

// 参数n,是要测定的数字,s是要内部测试的次数。

bool Rabin_Miller(int n,int s)

{

     for (int j = 0;j < s; ++j)

     {

          int a = rand()*(n-2)/RAND_MAX + 1;// 获得一个随机数1<=a<=n-1

         if (Witness(a,n))// 利用这个随即数和n进行判断对比,只要有一次返回true,就说明n不是一个素数

         {

              return false;

         }

     }

     return true;// 通过验证是一个素数

}

 

// 算法3:采用Rabin-Miller算法进行验算

// 这个算法是求大素数使用的。所以你的必须想办法支持大数字运算,

// 不然极易造成内存访问失效,我在我的机子上,MAX_NUMBER=10000时就会出现问题,1000就没有问题

void PrimeNumber3()

{

    

     int Max[MAX_NUMBER/2];// 在栈上分配,栈上空间要求一般都在2M之间,

     //   如果你需要更大空间,请在堆上申请空间(就是通过malloc,new来申请).素数的个数很少

     // 所以没有必要申请和所求数字同样大小的空间。

     int cout = 0;// 记录素数个数

     memset(Max,0,MAX_NUMBER/2);

 

     for(int i = 2; i < 1000; ++i)

     {

         if(Rabin_Miller(i,20))

         {

              Max[cout++] = i;

         }

     }

}

 

以上程序都经过测试,测试环境Window 2003+VC7.1

小结:以上只是简单介绍一下求素数的几种常用方法,实际方法远远不知这些。对于普通用户来说算法2就可以方便应用了。Rabin-miller算法,确实很高效,但是如果你对数学,编程知识不是很熟悉,建议不要使用。

如果使用过程中有任何疑问请与我联系。

QQ:35091551

MSN:ugg_xchj@163.com

下载源文件http://www.exuetang.net/article/NewsViewAdmin.aspx?NewsId=191

时间: 2024-12-30 13:50:16

求解素数几种方法的相关文章

c语言-C语言求素数算法,有几种方法可以降低时间复杂度

问题描述 C语言求素数算法,有几种方法可以降低时间复杂度 b可以非常大的时候,输出a到b之间素数的个数,怎么才能简化算法,降低运行时间 解决方案 采用列表法,每次找到新的素数,添加到表中.每次寻找素数,不用每个数字都尝试一次,而只要尝试小于这个数字的1/2的所有素数就可以了. 解决方案二: 具体做法 http://blog.csdn.net/liukehua123/article/details/5482854 解决方案三: 不需要b的1/2,只需要判断到b的根号2 解决方案四: http://

【转载】Python脚本判断一个数是否为素数的几种方法

     质数又称素数.指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数.素数在数论中有着很重要的地位.比1大但不是素数的数称为合数.1和0既非素数也非合数.质数是与合数相对立的两个概念,二者构成了数论当中最基础的定义之一.基于质数定义的基础之上而建立的问题有很多世界级的难题,如哥德巴赫猜想等.算术基本定理证明每个大于1的正整数都可以写成素数的乘积,并且这种乘积的形式是唯一的.这个定理的重要一点是,将1排斥在素数集合以外.如果1被认为是素数,那么这些严格的阐述就不得不加上

Python素数检测的方法

  这篇文章主要介绍了Python素数检测的方法,实例分析了Python素数检测的相关技巧,需要的朋友可以参考下 本文实例讲述了Python素数检测的方法.分享给大家供大家参考.具体如下: 因子检测: 检测因子,时间复杂度O(n^(1/2)) ? 1 2 3 4 5 6 7 def is_prime(n): if n < 2: return False for i in xrange(2, int(n**0.5+1)): if n%i == 0: return False return True

U盘无法格式化与U盘修复的几种方法

U盘由于种种不同的原因而格式化失败,提示如"windows无法完成格式化"之类.但是这并不就是意味着U盘就此要废弃了,试一试如下的方法(不过对于不长玩电脑的人来说是有点难度哈): 方法一: 1.点开始-运行-输入cmd-format f: /fs: fat32 (这里f:是指U盘所在盘符) 2.打开控制面板-管理工具-计算机管理-磁盘管理-找到U盘的所在的盘符--点右键--删除磁盘分区(这个选项是灰色的,没办法选取)-创建新的磁盘分区--按照提示完成磁盘分区后就可以格式化U盘了. 方法

【数值分析】误差的分析与减少及Matlab解线性方程的四种方法

1.误差的来源      模型误差:数学模型与实际问题之间的误差      观测误差:测量数据与实际数据的误差      方法误差:数学模型的精确解与数值方法得到的数值解之间的误差:例如      舍入误差:对数据进行四舍五入后产生的误差 2.减少误差的几种方法          现在,我们一般用计算机解决计算问题,使用最多的是Matlab软件.对实际问题进行数学建模时,可能存在模型误差,对数学模型进行数值求解时,我们使用的方法可能产生方法误差,我们输入计算机的数据一般是有测量误差的,计算机在运

mysql 复制表数据,表结构的3种方法

 什么时候我们会用到复制表?例如:我现在对一张表进行操作,但是怕误删数据,所以在同一个数据库中建一个表结构一样,表数据也一样的表,以作备份.如果用mysqldump比较麻烦,备份.MYD,.MYI这样的文件呢,操作起来也还是麻烦. 一,复制表结构 方法1: mysql> create table a like users; //复制表结构 Query OK, 0 rows affected (0.50 sec)   mysql> show tables; +------+ | Tables_i

JavaBean实现多文件上传的两种方法

上传 摘要:本文介绍了JavaBean实现多个文件上传的两种方法,分别是使用http协议和ftp协议实现.首先讲述了http协议传送多个文件的基本格式和实现上传的详细过程,之后简单介绍了使用ftpclient 类实现了ftp方式的上传,最后对这两种方法进行了比较. 关键字:JavaBean .http .ftp .ftpclient JavaBean是一种基于Java的软件组件.JSP对于在Web 应用中集成JavaBean组件提供了完善的支持.这种支持不仅能缩短开发时间(可以直接利用经测试和可

Word中输入立方米符号的三种方法

  Word中输入立方米符号的三种方法         Word中输入立方米符号方法一:输入法输入 其实现在有些输入法中集成了很多特殊符号,例如搜狗拼音中就有立方米符号,我们只需要打出立方米的拼音,就会出现一个立方米符号的选项. Word中输入立方米符号方法二:利用制作上标的方法 用制作上标的方法可以做出立方米符号的效果,但这种方法其实还可以细分为几种不同的操作,下面一一进行介绍. 一.在Word文档中输入3,然后将其选中,切换到"开始"选项卡,单击"上标"按钮即可

分析iOS Crash文件:符号化iOS Crash文件的3种方法

当你的应用提交到AppStore或者各个渠道之后,请问你多久会拿到crash文件?你如何分析crash文件的呢? 上传crash文件 你的应用应当有模块能够在应用程序crash的时候上传crash信息. 要么通过用户反馈拿到crash文件,要么借助自己或第3方的crash上传模块拿到crash文件. 今天要分析的场景是你拿到用户的.crash文件之后,如何符合化crash文件(Symbolicating crash logs)的3种方法.帮助尽快找到crash原因. crash文件例子 cras