素数推断算法(高效率)

chuanbindeng 的 素数推断算法

关于素数的算法是信息学竞赛和程序设计竞赛中常考的数论知识,在这里我跟大家讲一下寻找一定范围内素数的几个算法。看了以后相信

对大家一定有帮助。

正如大家都知道的那样,一个数 n 假设是合数,那么它的全部的因子不超过sqrt(n)--n的开方,那么我们能够用这个性质用最直观的方法

来求出小于等于n的全部的素数。

num = 0;

for(i=2; i<=n; i++)

{  for(j=2; j<=sqrt(i); j++)

if( j%i==0 ) break;

if( j>sqrt(i) ) prime[num++] = i;  //这个prime[]是int型,跟以下讲的不同。

}

这就是最一般的求解n以内素数的算法。复杂度是o(n*sqrt(n)),假设n非常小的话,这样的算法(事实上这是不是算法我都怀疑,没有水平。当然没

接触过程序竞赛之前我也仅仅会这一种求n以内素数的方法。-_-~)不会耗时非常多.

可是当n非常大的时候,比方n=10000000时,n*sqrt(n)>30000000000,数量级相当大。在一般的机子它不是一秒钟跑不出结果,它是好几分钟都跑不

出结果,这可不是我瞎掰的,想锻炼耐心的同学最好还是试一试~。。。。

在程序设计竞赛中就必需要设计出一种更好的算法要求能在几秒钟甚至一秒钟之内找出n以内的全部素数。于是就有了素数筛法。

(我表达得不清楚的话不要骂我,见到我的时候扁我一顿我不说一句话。。。)

素数筛法是这种:

1.开一个大的bool型数组prime[],大小就是n+1就能够了.先把全部的下标为奇数的标为true,下标为偶数的标为false.

2.然后:

for( i=3; i<=sqrt(n); i+=2 )

{   if(prime[i])

for( j=i+i; j<=n; j+=i ) prime[j]=false;

}

3.最后输出bool数组中的值为true的单元的下标,就是所求的n以内的素数了。

原理非常easy,就是当i是质(素)数的时候,i的全部的倍数必定是合数。假设i已经被推断不是质数了,那么再找到i后面的质数来把这个质

数的倍数筛掉。

一个简单的筛素数的过程:n=30。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

第 1 步过后2 4 ... 28 30这15个单元被标成false,其余为true。

第 2 步開始:

i=3;  因为prime[3]=true, 把prime[6], [9], [12], [15], [18], [21], [24], [27], [30]标为false.

i=4;  因为prime[4]=false,不在继续筛法步骤。

i=5;  因为prime[5]=true, 把prime[10],[15],[20],[25],[30]标为false.

i=6>sqrt(30)算法结束。

第 3 步把prime[]值为true的下标输出来:

for(i=2; i<=30; i++)

if(prime[i]) printf("%d ",i);

结果是 2 3 5 7 11 13 17 19 23 29

这就是最简单的素数筛选法,对于前面提到的10000000内的素数,用这个筛选法能够大大的减少时间复杂度。把一个仅仅见黑屏的算法

优化到立竿见影,一下就得到结果。关于这个算法的时间复杂度,我不会描写叙述,没看到过相似的记载。仅仅知道算法书上如是说:前几年比

较好的算法的复杂度为o(n),空间复杂度为o(n^(1/2)/logn).另外还有时间复杂度为o(n/logn),但空间复杂度为O(n/(lognloglogn))的算法。

我水平有限啦,自己分析不来。最有说服力的就是自己上机试一试。以下给出这两个算法的程序:

//最普通的方法:

#include<stdio.h>

#include<math.h>

#define N 10000001

int prime[N];

int main()

{

int i, j, num = 0;

for(i=2; i<N; i++)

{  for(j=2; j<=sqrt(i); j++)

if( j%i==0 ) break;

if( j>sqrt(i) ) prime[num++] = i;

}

for(i=2; i<100; i++) //因为输出将占用太多io时间,所以仅仅输出2-100内的素数。能够把100改为N

if( prime[i] )printf("%d ",i);

return 0;

}

//用了筛法的方法:

#include<stdio.h>

#include<math.h>

#define N 10000001

bool prime[N];

int main()

{

int i, j;

for(i=2; i<N; i++)

if(i%2) prime[i]=true;

else prime[i]=false;

for(i=3; i<=sqrt(N); i++)

{   if(prime[i])

for(j=i+i; j<N; j+=i) prime[i]=false;

}

for(i=2; i<100; i++)//因为输出将占用太多io时间,所以仅仅输出2-100内的素数。能够把100改为N

if( prime[i] )printf("%d ",i);

return 0;

}

装了vc的同学上机跑一下这两个程序试一试。这个区别,绝对是天上地下。前面那个程序绝对是n分钟黑屏的说。

另外,对于这种筛法,还能够进一步优化,就是bool型数组里面仅仅存奇数不存偶数。如定义prime[N],则0表示

3,1表示5,2表示7,3表示9...。假设prime[0]为true,则表示3时素数。prime[3]为false意味着9是合数。

这种优化不是简单的降低了一半的循环时间,比方依照原始的筛法,数组的下标就相应数。则在计算30以内素

数的时候3个步骤加起来走了15个单位时间。可是用这种优化则是这样:

则因为仅仅存3 5 7 9 11 13 15 17 19 21 23 25 27 29,仅仅须要14个单元

第 1 步 把14个单元赋为true (每一个单元代表的数是2*i+3,如第0单元代表3,第1单元代表5...)

第 2 步開始:

i=0;  因为prime[0]=true, 把 [3], [6], [9], [12]标为false.

i=1;  因为prime[1]=true, 把 [6], [11]标为false

i=2  2*i+3>sqrt(30)算法结束。

这样优化以后总共仅仅走6个单位时间。

当n相当大以后这种优化效果就更加明显,效率绝对不不过翻倍。

出了这种优化以外,另外在每一次用当前已得出的素数筛选后面的数的时候能够一步跳到已经被判定不是素数的

数后面,这样就降低了大量的反复计算。(比方我们看到的,i=0与i=1时都标了[6],这个就是反复的计算。)

我们能够发现一个规律,那就是3(即i=0)是从下标为[3]的開始筛的,5(即i=1)是从下标为[11]開始筛的(由于[6]

已经被3筛过了)。然后假设n非常大的话,继续筛。7(i=2)本来应该从下标为[9]開始筛,可是因为[9]被筛过了,而

[16]也已经被5(i=1)筛过了。于是7(i=2)从[23](就是2*23+3=49)開始筛。

于是外围循环为i时,内存循环的筛法是从 i+(2*i+3)*(i+1)即i*(2*i+6)+3開始筛的。

这个优化也对算法复杂度的减少起到了非常大的作用。

相比于一般的筛法,添?这两个优化后的筛法要高效非常多。高兴去的同学能够试着自己编敲代码看一看效率。我这里

有程序,须要的能够向我要。不懂得也能够问我。

上面的素数筛法是全部程序设计竞赛队员都必须掌握的,而后面加了两个优化的筛法是效率非常高的算法,是湖南大学

huicpc39同学设计的(可能是学来的,也可能是自创的。相当强悍)。在数量级更大的情况下就能够发现一般筛法和

优化后的筛法的明显差别。

另外,台湾的ACMTino同学也给我介绍了他的算法:a是素数,则下一个起点是a*a,把后面的全部的a*a+2*i*a筛掉。

这上面的全部的素数筛选的算法都能够再进一步化为二次筛选法,就是欲求n以内的素数,就先把sqrt(n)内的素数求

出来,用已经求得的素数来筛出后面的合数。

我把一般的筛选法的过程具体的叙述了一遍,应该都懂了吧?后面的优化过程及不同的方法,能看懂最好。不是非常难的。

相关知识:

最大公约数仅仅有1和它本身的数叫做质数(素数)——这个应该知道吧?-_-b

至今为止,没有不论什么人发现素数的分布规律,也没有人能用一个公式计算出全部的素数。关于素数的非常多的有趣的性质或者科学家的努力

我不在这里多说,大家有兴趣的话能够到百度或google搜一下。我在以下列出了一个网址,上面仅仅有个大概。很多其它的知识须要大家一点一点

地动手收集。

http://www.scitom.com.cn/discovery/universe/home01.html

1.高斯推測,n以内的素数个数大约与n/ln(n)相当,或者说,当n非常大时,两者数量级同样。这就是著名的素数定理。  

2.十七世纪费马推測,2的2^n次方+1,n=0,1,2…时是素数,这种数叫费马素数,可惜当n=5时,2^32+1就不是素数,

至今也没有找到第六个费马素数。

3.18世纪发现的最大素数是2^31-1,19世纪发现的最大素数是2^127-1,20世纪末人类已知的最大素数是2^859433-1,用十进制表示,这是一个258715位的数字。

4.孪生素数猜想:差为2的素数有无穷多对。眼下知道的最大的孪生素数是1159142985×2^2304-1和1159142985×2^2304+1。

5.歌德巴赫猜想:大于2的全部偶数均是两个素数的和,大于5的全部奇数均是三个素数之和。当中第二个猜想是第一个的自然推论,因此歌德巴赫猜想又被称为1+1问题。我国数学家陈景润证明了1+2,即全部大于2的偶数都是一个素数和仅仅有两个素数因数的合数的和。国际上称为陈氏定理。

素数推断算法(高效率),布布扣,bubuko.com

时间: 2024-08-25 11:41:16

素数推断算法(高效率)的相关文章

[email&#160;protected] Sieve of Eratosthenes (素数筛选算法) &amp; Related Problem (Return two prime numbers )

Sieve of Eratosthenes (素数筛选算法) Given a number n, print all primes smaller than or equal to n. It is also given that n is a small number. For example, if n is 10, the output should be “2, 3, 5, 7″. If n is 20, the output should be “2, 3, 5, 7, 11, 13,

Miller-Rabin素数测试算法

由费马小定理可以知道,若p是素数且a是整数,则满足a^p==a(mod p).若存在正整数a不满足a^p==a(mod p),那么n是合数. 定义:令a是一个正整数,若p是合数且满足a^p==a(mod p),则p称为以a为基的伪素数. Miller-Rabin素数测试算法原理: 假如p是素数,且(a,p)==1,(a为任意小于p的正整数),那么a^p-1==1(mod p).如果a^p-1==1(mod p), 则可认为n是素数,取多个底进行试验,次数越多,n为素数概率越大.(我的个人理解多次

Miller_Rabin素数测试算法模板对比

昨天在USACO做了一道判断素数的题,就想着学习一下Miller_Rabin素数测试算法,在网上找到两种模版,第一种十分简洁,运行速度也很快,但是会判错极少的几个非素数:第二种比较麻烦,运行速度很慢,所以我便想找到第一种模版不能判断的非素数特判一下,结果用了一天,电脑只找到10^8以下的,10^9内还有2个没找到,但正确的模版运行速度太慢,我的电脑又太渣,耗不起时间了,姑且先这样,等以后有深入理解有更好的方法再更新一下. 第一种:源自吉林大学ACM模版 刚开始用的是随机数测试,我想到以前了解过只

素数判断算法(python实现)

素数是只能被1与自身整除的数,根据定义,我们可以实现第一种算法. 算法一: def isprime(n): if n < 2: return False for i in range(2,int(math.sqrt(n))+1): if n % i == 0: return False return True 任意一个合数都可分解为素数因子的乘积,观察素数的分布可以发现:除 2,3 以外的素数必定分布在 6k (k为大于1的整数) 的两侧.6k % 6 == 0, (6k+2) % 2== 0,

素数判断算法(基于python实现)

素数是只能被1与自身整除的数,根据定义,我们可以实现第一种算法. 算法一: def isprime(n): if n < 2: return False for i in range(2,int(math.sqrt(n))+1): if n % i == 0: return False return True 任意一个合数都可分解为素数因子的乘积,观察素数的分布可以发现:除 2,3 以外的素数必定分布在 6k (k为大于1的整数) 的两侧.6k % 6 == 0, (6k+2) % 2== 0,

浅谈Miller-Rabin素数检测算法

浅谈Miller-Rabin素数检测 对于素数判断的操作,我们通常使用的是时间复杂度为\(O(\sqrt N)\)的试除法.按理说这种复杂度已经是较优秀的了,但是假如给定的需要判断的数极其之大,并且给定的时限不够以\(O(\sqrt N)\)的试除法来判断,该怎么办? 题出错了 想得美. 于是,今天的主角出场了:Miller-Rabin素数检测. Miller-Rabin素数检测算法用于在短时间内判断出一个数是否是质数,时间复杂度比试除法优秀,应该是\(O(T\times \log N)\)级别

素数判别算法

素数筛选法 原理就是当i是素数的时候,i的所有的倍数必然是合数.如果i已经被判断不是质数了,那么再找到i后面的质数来把这个质数的倍数筛掉.算法实现 1 #include <iostream> 2 #include <math.h> 3 using namespace std; 4 5 int main() 6 { 7 int N = 1000000; 8 //prime用来保存下标i是否是素数 9 bool prime[N+1]; 10 //初始化,偶数(除了2)都是合数 11 f

记一次使用快速幂与Miller-Rabin的大素数生成算法

大家都知道RSA的加密的安全性就是能够找到一个合适的大素数,而现在判断大素数的办法有许多,比如Fermat素性测试或者Miller-Rabin素性测试,而这里我用了Miller-Rabin素性测试的算法,具体的理论我写到下面. 算法的理论基础: Fermat定理:若n是奇素数,a是任意正整数(1≤ a≤ n?1),则 a^(n-1) ≡ 1 mod n. 2.  如果n是一个奇素数,将n?1表示成2^s*r的形式,r是奇数,a与n是互素的任何随机整数,那么a^r ≡ 1 mod n或者对某个j

Miller_Rabin(米勒拉宾)素数测试算法

首先需要知道两个定理: 1: 费马小定理: 假如p是素数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p). 2:二次探测定理:如果p是素数,x是小于p的正整数,且,那么要么x=1,要么x=p-1. 证明:这是显然的,因为相当于p能整除,也即p能整除(x+1)(x-1). 由于p是素数,那么只可能是x-1能被p整除(此时x=1) 或 x+1能被p整除(此时x=p-1). 接着 如果a^(n-1) ≡ 1 (mod n)成立,Miller-Rabin算法不是立即找另一个a进行测试,而是