求素数列表和判断素数的算法

合集下载

求素数的算法

求素数的算法

求素数的算法
1. 定义
素数又称质数,是指只能被1和它本身整除的自然数,如2、3、5、7、11等,而4、6、8、9等都不是素数。

2. 筛法
筛法是一种较为简单的求素数的算法,主要原理是先假设所有数都是素数,然后从小
到大开始筛选,将所有能够整除当前数字的数标记为合数,剩余的就是素数。

具体步骤如下:
(1)初始化数组:将从2到n的所有整数存入数组中,初始时都标记为素数。

(2)循环遍历数组:从2开始循环遍历数组,将当前数字标记为素数,然后将所有能够整除当前数字的数标记为合数。

(实际上只需要循环到sqrt(n)即可)
(3)输出素数:遍历数组,输出所有标记为素数的数。

3. 质数判定法
如果只需要判断某一个给定的数是否是素数,那么可以采用质数判定法。

常见的质数
判定法有以下几种:
(1)试除法:从2开始到sqrt(n)依次尝试除n,如果能够整除则不是素数,否则是
素数。

这种方法速度较慢,但实现简单。

(2)根号判定法:如果一个数n不是素数,那么n可以表示为两个整数的乘积,即n = x * y。

这两个整数必然有至少一个小于等于sqrt(n)。

因此,只需要判断是否存在小于等于sqrt(n)的因数即可。

(3)费马小定理:如果n是素数,那么对于任意整数a,a^(n-1) mod n = 1。

根据这个定理,我们可以随机选取一些a进行计算,如果a^(n-1) mod n 不等于 1,则n一定不是素数,否则n可能是素数。

(4)米勒-拉宾素性判定法:该方法是一种基于费马小定理的扩展算法。

具体实现过
程较为复杂,但速度较快,能够判断很大的素数。

求素数列表和判断素数的算法

求素数列表和判断素数的算法

求素数列表和判断素数的算法有兴趣阅读本文的读者,应该对素数概念是十分熟悉的了。

用计算机程序实现素数计算,集中在2个主要问题上:∙判断一个正整数是否是素数-(算法A)∙求一定范围内的素数列表- (算法B)关于素数的算法,根据素数的数学性质,大家都会想到如下几个方面:∙用遍历求模的方式判断素数∙素数都是奇数,可以在奇数数列中寻找素数∙利用开方来缩小搜索的范围然后,求素数的计算是复杂的,如果算法写得不好,则耗时较高。

在百度百科“素数”条目中的算法程序,是值得商榷的。

很多方法是O(N2)的算法。

为此,在本文中探讨了求素数列表和判断素数这两个算法,力图使算法可以达到O(N Log(N))优化级别。

在本文中,算法语言选用C#。

1,判断素数的简单实现(算法A-1)///<summary>///算法A-1,判断素数///</summary>///<param name="number">待测正整数</param>///<returns>是否为素数(为了简化,1以下的整数皆为素数)</returns>public static bool IsPrime(int number){// 为了简化,1以下的整数皆为素数if(number <= 2){return true;}// 奇偶性if (number % 2 == 0){return false;}// 利用开方缩小范围,优化效果十分明显int range = (int)Math.Sqrt(number) + 1;// 从3开始的奇数列for (int current = 3; current <= range; current += 2){// 判断是否为素数if (number % current == 0){// 合数return false;}}return true;}A-1算法中,首先判断奇偶性,再判断从3开始判断待查数(number)是否是合数,若到Math.Sqrt(number)还没有查到是合数,即为素数。

素数常见的算法

素数常见的算法

求素数的三种方法
素数的定义:
素数也叫质数。

一个大于1的自然数,除了1和它本身之外,不能被其它自然数整除的数叫做素数;能被其它自然数整除的数叫做合数。

规定,1既不是质数也不是合数。

法一:试除法(判断素数)
让N被2如果N能被其中任何一个整数整除,则提前结束循环,N不是素数;如果N不能被其中任何一个整数整除,则N是素数。

代码实现:
法二:埃氏筛法(求一个范围中所有素数)
试除法可以用来判断一个数是否为素数,如果用来求某一范围内所有素数的话,效率就比较低。

埃氏筛法是用来解决这类问题的古老而简单高效的方法,可以快速找到[2,]n中的所有素数。

具体操作是这样的:从2开始寻找素数,每次找到一个素数后就将它的倍数全部筛掉,并将该素数存储到另一个数组中,不断循环,直到原数组为空。

法三:欧拉筛法(埃氏筛法的优化版)
埃氏筛法中,由于一个数可以既是一个素数的倍数,又是另一个素数的倍数,可以发现这会出现重复标记的情况,即同一个数被筛掉了不止一次,浪费操作了。

欧拉筛法就是在埃氏筛法的基础上多了判断的步骤,从而消失了这种重复标记的情况,核心思想是用合数中的一个因数筛掉这个合数。

具体操作为:利用已经求得的素数,第一重循环将区间内的数从小到大遍历,第二重循环将以求得的素数从小到大遍历,将这个数和素数的乘积标记为合数。

如果一个数能被素数整除,跳出循环。

素数的算法[精华]

素数的算法[精华]

题目1:判断M是否为素数算法:让M被2到√M(M的开方)除,如果M能被2--√M之中任何一个整数整除,则提前结束循环,此时i必然小于或等于K(即√M);如果M不能被2--K(即√M)之间任何一整数整除,则在完成最后一次循环后,i还要加1,因此i=K+4,然后才终止循环.在循环之后判别i的值是否大于或等于K+1,若是,则表明未曾被2-K之间任一整数整除过,因此输出是"是素数".#include <iostream>#include <math.h>using namespace std;void main(){int i,k,m;cout<<"Please input the number:";while(cin>>m){k=sqrt(m);for(i=2;i<=k;i++)if(m%i==0) break;if(i>k)cout<<m<<" 是素数!"<<endl;elsecout<<m<<" 不是素数!"<<endl;}}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////题目2:求2--100之间的素数.在题目1的基础上再加一个外层循环就可满足要求.#include <iostream>#include <math.h>using namespace std;void main(){int m,k,i,n=0;for(m=2;m<=100;m++){k=sqrt(m);for(i=2;i<=k;i++)if(m%i==0)break;if(i>=k+1){cout<<m<<" ";n=n+1;}if(n%10==0)cout<<endl;}cout<<endl;}/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////题3:求2--100的质数(效率越高越好)算法:先定义一个长100的数组,代表100以内的数,数组内所有数全部置1,并假定1表示是素数,0则不是素数。

有关素数判断的一些算法(总结对比)

有关素数判断的一些算法(总结对比)

有关素数判断的⼀些算法(总结对⽐)素性测试是数论题中⽐较常⽤的⼀个技巧。

它可以很基础,也可以很⾼级(哲学)。

这次主要要介绍⼀下有关素数判断的奇技淫巧素数的判断主要分为两种:范围筛选型&&单个判断型我们先从范围筛选型这种常⽤的开始讲起,这⾥采⽤模板题来进⾏测试1.埃⽒筛这是最常⽤的筛法了,思路也很简单:任何⼀个素数的倍数都是合数然后我们O(n)扫⼀遍,同时筛去素数的倍数但是有⼀些数如6,会被2和3都筛去⼀次,就造成了效率上的浪费,所以复杂度经证明为**O(n log log n)CODE#include<cstdio>using namespace std;const int N=10000005;bool vis[N];int n,m,x;inline char tc(void){static char fl[100000],*A=fl,*B=fl;return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;}inline void read(int &x){x=0; char ch=tc();while (ch<'0'||ch>'9') ch=tc();while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();}inline void get_prime(int m){register int i,j;for (vis[1]=1,i=2;i<=m;++i)if (!vis[i]) for (j=i<<1;j<=m;j+=i) vis[j]=1;}int main(){//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);read(n); read(m); get_prime(n);while (m--){read(x);puts(vis[x]?"No":"Yes");}return 0;}2.线性筛(欧拉筛)这其实是对上者的优化,我们意识到⼀个数应该只有它的最⼩质因数删去,所以我们可以⼀边筛数的同时⼀边记录素数,这就是真正的O(n)复杂度CODE#include<cstdio>using namespace std;const int N=10000005;int prime[N],n,m,x,cnt;bool vis[N];inline char tc(void){static char fl[100000],*A=fl,*B=fl;return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;}inline void read(int &x){x=0; char ch=tc();while (ch<'0'||ch>'9') ch=tc();while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();}inline void Euler(int n){register int i,j;for (vis[1]=1,i=2;i<=n;++i){if (!vis[i]) prime[++cnt]=i;for (j=1;j<=cnt&&i*prime[j]<=n;++j){vis[i*prime[j]]=1;if (!(i%prime[j])) break;}}int main(){//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);read(n); read(m);Euler(n);while (m--){read(x);puts(vis[x]?"No":"Yes");}return 0;}注意上⾯的那句话:if (!(i%prime[j])) break;这保证了线性筛的效率,不会产⽣重复,因为当i%prime[j]==0时这个数就是让后⾯的数删去。

素数的算法原理和应用

素数的算法原理和应用

素数的算法原理和应用概述素数是指只能被1和自身整除的正整数。

素数在密码学、计算机科学和数学研究等领域具有重要的应用。

本文将介绍素数的算法原理以及在实际应用中的一些常见场景。

素数的判断算法判断一个数是否为素数是素数算法的基础。

常用的素数判定算法有两种:试除法和素数筛法。

试除法试除法是最简单直观的素数判定方法。

对于一个待判断的正整数n,只需从2开始遍历到sqrt(n)(即n的平方根)的整数m,检查是否有任何m能整除n。

若能找到能整除n的m,则n不是素数;否则,n是素数。

试除法的时间复杂度为O(sqrt(n)),适用于判断大部分整数是否是素数。

然而,对于非常大的数,这种方法的效率较低。

素数筛法素数筛法通过筛选法来判断素数。

其中最常用的是埃拉托斯特尼筛法。

首先,生成一个长度为n+1的布尔类型数组,将其初始值都设为true。

然后从2开始遍历到sqrt(n)的整数m,在数组中将2的倍数、3的倍数、4的倍数…全部标记为false。

最后,数组中值为true的索引对应的数就是素数。

素数筛法的时间复杂度为O(nloglogn),虽然比试除法高效,但由于需要生成一个长度为n+1的数组,对于非常庞大的数,也存在一定的限制。

素数的应用素数在密码学、计算机科学和数学研究等领域有广泛的应用。

以下是一些常见的素数应用场景。

密码学中的应用素数在密码学中起到至关重要的作用,特别是在公钥密码学中。

其中一个常见的应用是RSA加密算法。

在RSA算法中,首先需要生成两个大素数p和q,然后计算它们的乘积n=p*q。

n被用作加密和解密过程中的模数,而p和q用于生成公钥和私钥。

素数的随机性应用素数的随机分布属性使其成为生成随机数的重要组成部分。

例如,质数的随机分布性质被广泛应用在随机数生成算法中,确保生成的随机数能够满足安全性和随机性的要求。

整数因子分解素数在整数因子分解中也有重要应用。

由于素数只能被1和自身整除,因此在将一个大数分解成其因子时,可以使用素数的概念来加快计算过程。

求100以内的素数(质数)算法梳理

求100以内的素数(质数)算法梳理

求100以内的素数(质数)算法梳理质数定理:1、从2开始到⾃⾝的-1的数中找到⼀个能整除的(从2开始到⾃⾝开平⽅的数中找到⼀个能整除的)。

2、⼀个合数⼀定可以分解成⼏个质数的乘积,也就是说,⼀个数如果能被⼀个质数整除就是合数。

(使⽤列表保存质数)使⽤定理1的基本写法:(1)n = 100for i in range(2, n):for j in range(2, i):if i % j == 0:breakelse:print(i, end='')这种基本写法效率不⾼,有2点可以改进的地⽅:1、第⼀层循环的i取值时,因为偶数确定不是质数,所以排除偶数,使⽤range()函数排除偶数,range(3, n, 2)这样就减少了⼀半的数。

2、第⼆层循环j取值时,考虑从2开始到i开平⽅取值,同时也把偶数排除range(3, int(i**0.5)+1, 2)这样也可减少⼀半的数。

2就是质数,单独打印。

(2)改进(1)n = 100print(2)for i in range(3, n, 2):for j in range(3, int(i**0.5)+1, 2):if i % j == 0:breakelse:print(i, end='')(3)再(2)的基础上还有优化的点,发现第⼀层循环i取值时,当i>10时,5的倍数也可排除n = 100print(2)for i in range(3, n, 2):if i > 10 and i % 5 == 0:continuefor j in range(3, int(i**0.5)+1, 2):if i % j == 0:breakelse:print(i, end='')(4)利⽤定理2,⽤列表保存上⼀次的运算结果n = 100L = [2]for i in range(3, n, 2):for j in L:if i % j == 0:breakelse:L.append(i)print(L)此种写法的效率不⾼,第⼀层循环的i没必要与列表中的每⼀个元素取余,与从2开始到i的开平⽅处之间的数取余即可。

素数判断问题

素数判断问题

素数判断问题素数是指只能被1和本身整除的自然数。

对于给定的一个数,判断它是否为素数是一个常见且有趣的数学问题。

在本文中,我们将介绍几种素数判断的方法,并通过代码示例来实现。

一、暴力法暴力法是最直接的素数判断方法,即对于给定的数n,遍历从2到n-1的数进行整除运算,如果存在能整除n的数,则n不是素数;否则,n为素数。

代码实现如下:```pythondef is_prime(n):if n <= 1:return Falsefor i in range(2, n):if n % i == 0:return Falsereturn True```以上代码中,我们首先判断n是否小于等于1,因为1不是素数。

然后从2开始遍历到n-1,如果n能被任意一个数整除,则返回False,否则返回True。

这种方法简单易懂,但效率较低。

当n较大时,会进行大量的除法运算,耗费较长的时间。

二、优化方法在暴力法的基础上,我们可以进行一些优化,使判断素数的效率提高。

1. 去除偶数判断:除了2之外,所有的偶数都不可能是素数。

我们可以对代码进行优化,使其在判断n为偶数时直接返回False,减少不必要的运算。

代码实现如下:```pythondef is_prime(n):if n <= 1:return Falseif n == 2:return Trueif n % 2 == 0:return Falsefor i in range(3, int(n ** 0.5) + 1, 2):if n % i == 0:return Falsereturn True```2. 优化循环范围:对于一个数n,如果不存在小于等于n的因子,那么大于n的数也不可能是其因子。

我们只需要判断从2到√n的范围即可。

代码实现如下:```pythondef is_prime(n):if n <= 1:return Falseif n == 2:return Trueif n % 2 == 0:return Falsefor i in range(3, int(n ** 0.5) + 1, 2):if n % i == 0:return Falsereturn True```以上代码中,我们利用`int(n ** 0.5)`可以获取n的平方根,并将循环范围缩小到2到√n。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

求素数列表和判断素数的算法有兴趣阅读本文的读者,应该对素数概念是十分熟悉的了。

用计算机程序实现素数计算,集中在2个主要问题上:∙判断一个正整数是否是素数-(算法A)∙求一定范围内的素数列表- (算法B)关于素数的算法,根据素数的数学性质,大家都会想到如下几个方面:∙用遍历求模的方式判断素数∙素数都是奇数,可以在奇数数列中寻找素数∙利用开方来缩小搜索的范围然后,求素数的计算是复杂的,如果算法写得不好,则耗时较高。

在百度百科“素数”条目中的算法程序,是值得商榷的。

很多方法是O(N2)的算法。

为此,在本文中探讨了求素数列表和判断素数这两个算法,力图使算法可以达到O(N Log(N))优化级别。

在本文中,算法语言选用C#。

1,判断素数的简单实现(算法A-1)///<summary>///算法A-1,判断素数///</summary>///<param name="number">待测正整数</param>///<returns>是否为素数(为了简化,1以下的整数皆为素数)</returns>public static bool IsPrime(int number){// 为了简化,1以下的整数皆为素数if(number <= 2){return true;}// 奇偶性if (number % 2 == 0){return false;}// 利用开方缩小范围,优化效果十分明显int range = (int)Math.Sqrt(number) + 1;// 从3开始的奇数列for (int current = 3; current <= range; current += 2){// 判断是否为素数if (number % current == 0){// 合数return false;}}return true;}A-1算法中,首先判断奇偶性,再判断从3开始判断待查数(number)是否是合数,若到Math.Sqrt(number)还没有查到是合数,即为素数。

这个方法是O(N1/2)级别的算法,所以很快,即使是最悲观的int.MaxValue,在本人的本机计算也可达到125ms的运行时间。

2,判断素数的优化算法(A-2)虽然这种算法速度比较快,但如果频繁使用A-1算法,累计起来开销就比较大了,假设有N次访问的话,算法级别就到了O(N3/2)。

因此,本人的解决方案是先计算出一定范围内的素数列表存放在哈希表中,多次调用就可以忽略查询时间了。

///<summary>///素数的Hash表///</summary>private static HashSet<int> Primes { get; set; }///<summary>///算法A-2,判断素数///</summary>///<param name="number">待测正整数</param>///<returns>是否为素数(为了简化,1以下的整数皆为素数)</returns>public static bool IsPrime2(int number){// 为了简化,1以下的整数皆为素数if (number <= 2){return true;}return Primes.Contains(number);}这种算法的前提是要求一个素数表,这就是算法B要研究的问题了。

3 求素数列表的简单算法(B-1)///<summary>///求素数列表的简单算法(算法B-1)///</summary>///<param name="range"></param>///<returns></returns>public static int[] GetPrimes(int range){List<int> primeList = new List<int>();// 从3开始的奇数列for(int current = 3; current <= range; current += 2){// 判断是否为素数bool isPrime = true;foreach (int prime in primeList){if (current % prime == 0){// 若为合数,进入下一数的判断isPrime = false;break;}}if (isPrime){// 加入素数列表primeList.Add(current);}}// 把2补入primeList.Insert(0, 2);return primeList.ToArray();}上述算法,是对小于N的数,遍历素数表,判断是否为合数,全不成立的即为素数,并加入素数表。

这个算法的一个特点是只对奇数数列进行遍历,从而使遍历次数减少了一半。

4,步长优化的算法(算法B-2)有读者会想到阴阳素数数列的问题,也就是除了2,3以外,素数要么在(6N-1)数列,要么在(6N+1)数列,这样,是不是就可以通过改变步长方式就可以提高速度?是的,本人也做过这样的实验,代码如下:///<summary>///求素数列表的简单算法(算法B-2)///</summary>///<param name="range"></param>///<returns></returns>public static int[] GetPrimes2(int range){List<int> primeList = new List<int>();int index = 1; // 步长下标int[] loops = new int[] { 2, 4 };//步长for (int current = 5; current <= maxValue; current += loops[index]) {// 判断是否为素数bool isPrime = true;foreach (int prime in primeList){if (current % prime == 0){// 若为合数,进入下一数的判断isPrime = false;break;}}if (isPrime){// 加入素数列表primeList.Add(current);}index ^= 1; //阴阳数列转换}// 把2,3补入primeList.Insert(0, 2);primeList.Insert(0, 3);return primeList.ToArray();}采用步长数组和异或操作切换,可以最大限度的不增加代码优化后的开销。

这样的优化取得了一定的优化效果,但效果不明显。

我分析,B-1是最多达到了50%的优化,而B-2是达到了33.3%的优化效果,所以优化不明显。

所以后面的算法,就忽略了B-2的这样的优化。

5,采用开方法优化的算法(算法-B-3)本文的重点来了求素数列表的一大挑战,是当求解的范围很大时,速度就会变得其慢。

在对int.MaxValue/ 10000规模计算中,大致是1910ms左右,在往上速度就很慢了。

分析原因,算法中的最大的瓶颈在于求模操作。

由于求模操作命中率不高,所以执行次数很多。

根据数论知识,可以得到下来推论:把全集X = {n|n <= N}, 可以分为两个子集,X1={n | n <= N1/2 + 1}和X2={n | N1/2 + 1 < n <=N}, Y1是X1中的素数子集, Y2是X2中的素数子集,则X2中的所有合数的因数,全属于Y1。

(推论-1)这个推论告诉我们,我们可以把算法分成两部分,按B-1方法求1到 N1/2 + 1部分的素数(Y1),在把N1/2 + 1到N的数求模一次求素数(而不是遍历素数表),得到Y2,Y1+Y2就是全部素数表。

///<summary>///平方法(算法B-3)///</summary>///<returns></returns>public static int[] GetPrimes3(int maxValue){int range = (int)Math.Sqrt(maxValue);// 素数列表List<int> primeList = new List<int>();int current = 3;// 从3开始的奇数列for (; current <= range; current += 2){// 判断是否为素数bool isPrime = true;foreach (int prime in primeList){if (current % prime == 0){// 若为合数,进入下一数的判断isPrime = false;break;}}if (isPrime){// 加入素数列表primeList.Add(current);}}List<int> primeList2 = new List<int>();// 从3开始的奇数列for (; current <= maxValue; current += 2){// 判断是否为素数bool isPrime = true;foreach (int prime in primeList){if (current % prime == 0){// 若为合数,进入下一数的判断isPrime = false;break;}}if (isPrime){// 加入素数列表primeList2.Add(current);}}// 把2补入primeList.Insert(0, 2);primeList.AddRange(primeList2);return primeList.ToArray();}这样的修改将使运行效率提高了近100倍。

6,利用筛法进行更高级的优化(算法B-4)对于上述用例,主要对19980个素数的二次循环上,在从N1/2到N之间的数中,进行log(M1)的循环(基数是623)和求模。

所以这个算法的复杂度是O(log(M1)*(N- N1/2))。

相关文档
最新文档