素数专题——素数筛法

关于素数的判断,大家最常用的方法估计就是循环判断到sqrt(n)的方法了:(直接上代码)

bool isprime(int n)
{
	for(int i=2;i<=sqrt(n);i++)
	{
		if(n%i==0)
		return false;
	}
	return true;
}

  

这种素数的判断方法的确直观,但这种算法只对较小数据量适用,当数据量较大时,该方法就不再适用于素数的判定了。因此,我们此处引入一种新的算法——素数筛法。

首先介绍一下什么叫素数筛法:

假设所有待判断的数字的上限是L,声明一个长度为L+1的布尔数组A[L+1]。用这个数组来表示对应下标的数字是不是素数。起初,将数组所有成员标记为1,然后按照某种方法将其中的非素数都标记为0即可,完成后的数组有这样的特征:所有素数为下标的成员内存的数字都是1,所有非素数为下标的成员内存的数字都是0。例如 :2 是素数,那么A[2]=1;4不是素数,那么A[4]=0。因此,我们获取了一个素数表。这样,判断一个数是不是素数,直接查找即可。这样,我们在使用素数的时候就无需再进行素数的判断,这将大大缩短程序的运行时间,虽然我们需要提前计算素数表,但这相比较于用的时候再进行素数的判定,无疑是一个巨大的进步。下面介绍如何进行素数的标记:

这个标记的方法是这样的:1不是质数,也不是合数,标记0。第二个数2是质数标记为1,而把2后面所有能被2整除的数都标记为0。2后面第一个没划去的数是3,把3标记为1,再把3后面所有能被3整除的数都标记为0。3后面第一个没划去的数是5,把5标记为1,再把5后面所有能被5整除的数都标记为0。这样一直做下去,就会把不超过N的全部合数都标记为0,留下的就是不超过N的全部质数。因为希腊人是把数写在涂腊的板上,每要划去一个数,就在上面记以小点,寻求质数的工作完毕后,这许多小点就像一个筛子,所以就把埃拉托斯特尼的方法叫做“埃拉托斯特尼筛法”,简称“筛法”。

 1 int prime[MAXN];//素数数组
 2 bool isprime[MAXN + 10];//is_prime[i]表示i是素数
 3 //返回n以内素数的个数
 4 int sieve(int n)
 5 {
 6     int p = 0;//素数个数计数器
 7     for (int i = 0; i <= n; i++)
 8         is_prime[i] = true;
 9     is_prime[0] = is_pri[1] = false;//首先标记0和1不是素数
10     for (int i = 2; i <= n; i++)
11     {
12         if (is_prime[i]) //如果i是素数
13         {
14             prime[++p] = i;//将素数放进素数表
15             for (int j = 2 * i; j <= n; j += i)//所有i的倍数都不是素数
16                 is_prime[j] = false;
17         }
18     }
19     return p;
20 }

当然这个方法已经有很大的改进了,但是仍然存在会有重复筛掉某一合数,增加无用计算的现象,例如,删3的倍数时15标记为0,删15的倍数时,同样再一次将15标记为0。这还不够精简,因此有人将这个方法进行了改进:

首先初始A[1]=0,A[2]=1;

其次,将所有偶数下标的成员全部置为0,

接下来求出 t=√A

接下来从i=3开始到i=t开始当A[i]=1时进行下列操作

从 j=i*i 开始,到 j=L 结束,每次 j 加 2*i 让A[j]=0

改进原理如下:

将1和2进行初始化很容易理解

接着是将所有的偶数标记为0,这也很好理解,因为除了2之外所有的偶数都不是素数,这样一来,数据处理量直接缩小一半,爽!!!!

接下来就是问题的关键了,也是比较难理解的一部分,为什么再循环判断的时候到√A 就结束了哪?我们是这样解释的,因为在内部循环中我们是让 j 从i * i 开始循环的,如果 i > √A ,呢么我们一定可以保证 i *  i 是大于 L 的,所有就没有必要再继续循环了。内循环的处理原理是这样的:实际上我们处理的是 i 的倍数是不是素数的问题.

为什么 i 从3 开始 到 t 便可以结束呢?原因是循环之内(黄色字体)的 j 每次是以 i * i 为初始值进行判断的, 如果 i > t ,那么 i * i 一定 大于 L,所以 就没必要进行 t 之后的循环了。只判断 A[ i ]= 1(即 A[ i ]是奇数)的情况,原因是 循环之内的处理,实际上处理的是 i 的倍数是不是素数的问题,大家都清楚,不仅 2 的所有倍数是偶数,所有偶数的倍数都是偶数。

在解释循环之内(黄色字体)的内容,有人很好奇,为什么不用考虑     i *3 到 i *( i-1)之间的数呢,那么假设 有一个数 p 介于 3 和( i - 1)之间, 显然 ,如果 i * p 是小于 L 范围之内的数, 在 i = p

的时候,就应该判断过这个数了。

j = i * i 是非素数,这个就很明显了。

至于为什么 j 每次的增量是 2 * i ,而不是 i 呢?因为奇数个奇数相加一定是奇数,偶数个奇数相加一定是偶数。首先我们已经在判断时保证 i 是 奇数,那么 i 个i 相加就可以表示为 i * i,如果增量是 i 且 i * i

是奇数的话,i * i +i 必定是偶数, i*i+2i 才是奇数,也就说增量是 i 的时候,每两次循环中,有一次 就判断偶数(偶数之前已经被排除过了),这样岂不是违背了要提高效率的初衷?因此,在当前循环中需

要处理的只是 奇数且是非素数的情况。j =i * i+2i=i *( i + 2)显然也是非素数,以此类推 j = j +2*i 是非素数,直至该数超过上限结束本次循环。

这样就保证万无一失且不重复的排除所有情况啦。

好了直接上代码

 1 int prime[MAXN];//素数数组
 2 bool isprime[MAXN + 10];//is_pri[i]表示i是素数
 3 //返回n以内素数的个数
 4 int sieve(int n)
 5 {
 6     int p = 0;//素数个数计数器
 7     for (int i = 0; i <= n; i++)
 8         is_prime[i] = true;
 9     is_prime[0] = is_prime[1] = false;//首先标记0和1不是素数
10     is_prime[2] = true;//标记2是素数
11
12     for (int i = 2; i <= sqrt(n); i++)
13     {
14         if (is_prime[i]) //如果i是素数
15         {
16             prime[++p] = i;//将素数放进素数表
17             for (int j = i * i; j <= n; j += 2 * i)//所有i的倍数都不是素数
18                 is_prime[j] = false;
19         }
20     }
21     return p;
22 }

原文地址:https://www.cnblogs.com/jiameng991010/p/11123695.html

时间: 2024-11-07 12:54:16

素数专题——素数筛法的相关文章

素数专题

素数专题 素数是一个经常的涉及到得内容,所以有必要整理出有关解决素数相关问题的算法 学习资料:Eratosthenes筛法和欧拉筛法对比 一般筛法求素数+快速线性筛法求素数 数学技巧之素数筛选 1 /* 2 约数枚举,复杂度O (sqrt(n)) 3 By TiaoZhan 4 */ 5 vector<int> divisor(int n) 6 { 7 vector<int> res; 8 for (int i=1; i*i<=n; ++i) 9 { 10 if (n % i

【数学】【数论】素数的线性筛法

写在前面 记录了个人的学习过程,同时方便复习 素数的线性筛法 有时候需要筛出来一张素数表,即1~n范围内的所有素数 一个个枚举判断是否为素数显然太慢 于是经过仔细的研究之后,发现如果存在正整数k(k>2)不是素数,那么它的因子里面一定包含之前的素数 这样的话,开一个boolean数组标记一下不是素数的数,筛到它们的时候跳过就好 详见埃拉托斯特尼筛法 但是如果这样筛,显然会有重复的筛除啊 比如6筛去了42,7也筛去了42 这样的情况还有很多很多,十分影响效率,时间上并不是线性的 但如果按照一个数的

判断2~100的所有素数,是素数输出并打印

/** * 判断2~100的所有素数,是素数输出并打印 * 10个一换行 */ public class IsPrime { public static void main(String[] args) { int num = 100; printPrime(num); } public static void printPrime(int num) { int count = 0; //记素数的个数 /*检验2~num之间所有的数字*/ for (int i = 2; i <= num; i+

数论快速入门(同余、扩展欧几里德、中国剩余定理、大素数测定和整数分解、素数三种筛法、欧拉函数以及各种模板)

数学渣渣愉快的玩了一把数论,来总结一下几种常用的算法入门,不过鶸也是刚刚入门, 所以也只是粗略的记录下原理,贴下模板,以及入门题目(感受下模板怎么用的) (PS:文中蓝色字体都可以点进去查看百度原文) 附赠数论入门训练专题:点我打开专题(题目顺序基本正常,用以配套数论入门) 一.同余定理 简单粗暴的说就是:若 a-b == m 那么 a%m == b%m 这个模运算性质一眼看出...直接上入门水题: Reduced ID Numbers 附AC代码(这个也没啥模板....知道就好) #inclu

素数 专题

素数性质总结: 小于x的素数个数(随着x逐渐增大),与x/lnx近似: 素数测试方法,诶拉托色尼筛法:如果n是一个合数,那么n一定有一个不超过sqrt(n)的素因子:6N±1法:对于任何一个自然数,都可以表示为如下形式之一:6N,6N+1,6N+2,6N+3,6N+4,6N+5(N=0,1,2,3...)显然,当N>=1时,只有形如6N+1,6N+5的自然数有可能是素数(代码后面贴上) n!的素因子分解中的素数p的幂为 n/p+n/p2+n/p3+...... 梅森素数:如果m是一个正整数,且2

一般筛法求素数+快速线性筛法求素数

素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功. 基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2 ..N^(0.5) ,看看能否整除N. 如果需要判断的次数较多,则先用下面介绍的办法预处理. 一般的线性筛法 首先先介绍一般的线性筛法求素数 void make_prime() { memset(prime, 1, sizeof(prime)); prime[0]=false; prime[1]=false; int N=31700; for (int i=2; i<

【转载】一般筛法求素数+快速线性筛法求素数

素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功. 基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2 ..N^(0.5) ,看看能否整除N. 如果需要判断的次数较多,则先用下面介绍的办法预处理. 一般的线性筛法 首先先介绍一般的线性筛法求素数 void make_prime() { memset(prime, 1, sizeof(prime)); prime[0]=false; prime[1]=false; int N=31700; for (int i=2; i<

【算法】素数专题

[素数判定] 假设输入的都是正数,时间复杂度O(sqrt(n)) bool is_prime(int n){ for(int i = 2;i*i <= n; i++){ if(n % i == 0) return false; } return n != 1; } [因数枚举] 时间复杂度O(sqrt(n)). vector<int> divisor(int n){ vector<int> res; for(int i = 1;i*i <= n; i++){ if(n

素数的一般筛法和快速线性筛法

一般筛法: 1 void prime() { 2 memset(vis, true, sizeof(vis)); 3 vis[0] = vis[1] = false; 4 for (int i = 2; i < maxn; i++) { 5 for (int j = 2; i * j < maxn; j++) { 6 vis[i * j] = false; 7 } 8 } 9 } 快速线性筛法: 1 int prime[N] = {0}: 2 bool isNotPrime[N] = {1,