C++几种常见的素数判断算法

求解一个算法,我们首先要知道它的数学含义.依据这个原则,首先我们要知道什么是素数.; 素数是这样的整数,它除了表示为它自己和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问题,我一直没有找到那篇论文,听说里面有很多数学理论。如果那位大人有这篇论文,麻烦转发一份。

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

以下算法我们不涉及内存溢出,以及大数字的问题。如果测试数字超过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_NUMBER2]; 在栈上分配,栈上空间要求一般都在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_NUMBER2]; 在栈上分配,栈上空间要求一般都在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如果任意一式成立,我们就说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) 如果j0且z=1, 那麽p不是素数

(5) 设j=j+1。如果j且zp-1,设z=z^2 mod p,然后回到(4)。如果z=p-1,那麽p通过测试,可能为素数。

(6) 如果j=b 且zp-1,不是素数

判定是否存在 a^s=1 (mod n) 或a^((2^j)s)= -1(mod n)(0=j

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=(dd)%n;

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

{

return 1;

}

if ((n-1)&(10))

{

d=(da)%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_NUMBER2]; 在栈上分配,栈上空间要求一般都在2M之间,

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

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

int cout = 0; 记录素数个数

memset(Max,0,MAX_NUMBER2);

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

{

if(Rabin_Miller(i,20))

{

Max[cout++] = i;

}

}

}

(0)

相关推荐

  • 配置本地路由的几种常见方法介绍

    本文主要和大家分享如何配置本地路由的几种常见方法,希望给大家提供多一些网络基础知识! 为了有效提高工作效率,不少规模较大的单位把局域网按照一定的规律分成了许多不同用途的子网,要想让不同子网之间相互能够 ...

  • 几种常见的商务ppt版面布局

    很多人都没有进行过有关平面设计的学习,但是也能做好ppt,就是因为大多都用到了模板,自己做多了看多了就自然而然的会做ppt了。这里给大家介绍几种商务ppt版面的布局。 1上下布局:一般是从上到下的顺序 ...

  • 几种常见的屏蔽电脑USB接口的方法

    如何控制电脑USB接口? USB是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。USB接口即插即用和热插拔功能。USB接口可连接127种外设,如鼠标和键盘等。它已成为当今电脑与大量智能设备的必 ...

  • 七种常见的Word打印设置技巧

    七种常见的Word打印设置技巧 1.打印指定页码 有些时候,我们只希望打印文档中的某些页码,只要点击菜单命令"文件→打印",在打开的"打印"对话框中,选中&qu ...

  • 用Excel制作工资条的几种常见方法

    用Excel制作工资条的几种常见方法 一.复制粘贴法 大家最容易想的方法就是复制标题行,在每一位员工数据行的上方粘贴,手动完成工资条的制作: 1.选择A1:G1单元格,复制标题行. 2.右击第3行行号 ...

  • PPT几种常见的处理方法

    PPT几种常见的处理方法 以图片和图例型文字为主,减少大篇纯文字的使用,增强视觉化 页面中夹杂一些非常规的版式,打破规则,更具活泼和视觉感 减少设计中的过多装饰,摒除冗杂,适当使用单纯的线条.色块,会 ...

  • 八种常见Excel错误提示及问题解决方法

    八种常见Excel错误提示及问题解决方法 1.#####! 原因:如果单元格所含的数字.日期或时间比单元格宽,或者单元格的日期时间公式产生了一个负值,就会产生#####!错误. 解决方法:如果单元格所 ...

  • win7系统黑屏了怎么办?win7系统电脑发生黑屏的九种常见原因及解决方法

    说到win7系统电脑黑屏问题,相信大家都不会陌生了,而导致win7系统电脑黑屏的原因有很多种,许多电脑小白遇到黑屏状况就束手无策了,不知道怎么解决.所以今天小编给大家讲解win7系统电脑发生黑屏八种常 ...

  • java代码两种常见的执行方法

    java代码两种常见的执行方法详细说明 操作方法 01 打开eclipse开发环境(可以用其他的开发环境人员MyEclipse等) 02 创建或者打开一个java project 新建一个java文件 ...