ACM 进阶学习第一课----素数相关(2)
2013-11-05 00:59
281 查看
今天学习数论的第二个方面
素数相关
欧拉定理
素数测试
Pollard rho方法
取出容器中最小的数p,p一定是质数,删去p的所有倍数(注:只需从p2开始删除即可)
重复上述步骤直到容器为空
缺陷:一个数可能被重复删去多次,影响效率
例:在p=2,3,5,7时均会尝试删除210
一般的,若x有k个质因子,则x会被处理k次
取出容器中最小的数p,p一定是质数
删去所有的pi,令q为第一个未被删除的数,保留q,删去所有的piq,再令q为下一个未被删除的数...直到q遍历所有未被删除的数为止(这一步骤可以把最小质因数为p的所有数删去)
重复上面两个步骤直到容器为空
也即是:
1.开一个大的bool型数组prime[],大小就是n+1就可以了.先把所有的下标为奇数的标为true,下标为偶数的标为false.
2.然后:
3.最后输出bool数组中的值为true的单元的下标,就是所求的n以内的素数了。
原理很简单,就是当i是质(素)数的时候,i的所有的倍数必然是合数。如果i已经被判断不是质数了,那么再找到i后面的质数来把这个质
数的倍数筛掉。
运行结果:
如定义prime
,则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开始筛的。
这个优化也对算法复杂度的降低起到了很大的作用。
相比于一般的筛法,加入这两个优化后的筛法要高效很多。
时间有点晚了,欧拉函数早上再搞。
【未完待续】。。
素数相关
主要内容
算术基本定理欧拉定理
素数测试
Pollard rho方法
算术基本定理
筛法
目标:求出n以内的所有质数【原始算法步骤】
初始时容器内为2到n的所有正整数取出容器中最小的数p,p一定是质数,删去p的所有倍数(注:只需从p2开始删除即可)
重复上述步骤直到容器为空
【原始算法分析】
优点:算法简单,空间上只需要一个O(n)的bool数组缺陷:一个数可能被重复删去多次,影响效率
例:在p=2,3,5,7时均会尝试删除210
一般的,若x有k个质因子,则x会被处理k次
【算法改进】
初始时容器内为2到n的所有正整数取出容器中最小的数p,p一定是质数
删去所有的pi,令q为第一个未被删除的数,保留q,删去所有的piq,再令q为下一个未被删除的数...直到q遍历所有未被删除的数为止(这一步骤可以把最小质因数为p的所有数删去)
重复上面两个步骤直到容器为空
也即是:
1.开一个大的bool型数组prime[],大小就是n+1就可以了.先把所有的下标为奇数的标为true,下标为偶数的标为false.
2.然后:
for( i=3; i<=sqrt(n); i+=2 ) { if(prime) for( j=i+i; j<=n; j+=i ) prime[j]=false; }
3.最后输出bool数组中的值为true的单元的下标,就是所求的n以内的素数了。
原理很简单,就是当i是质(素)数的时候,i的所有的倍数必然是合数。如果i已经被判断不是质数了,那么再找到i后面的质数来把这个质
数的倍数筛掉。
示例代码
#include<iostream> #include<math.h> #include<stdlib.h> using namespace std; const int MAXV = 100; //素数表范围 bool flag[MAXV+1]; //标志一个数是否为素数 int prime[MAXV+1]; //素数表,下标从0开始 int size=0; //素数个数 void genPrime(int max) { memset(flag, true, sizeof(flag));//首先对标签数组进行初始化,全部设为true。 for(int i = 2; i <= max / 2; i++) { /* 从2开始,删除2的倍数 */ if(flag[i]) { //j=i<<1等价于 j=i*2,即j是i的两倍,而最后的j+=i,则表示下一个循环j是i的3倍,接着4倍。。。 //i的所有2~N倍数肯定都不是素数,因此将flag置为0,直到最后一位。 for(int j = i << 1 ; j <= max; j += i) { flag[j] = false; } } } for(i = 2 ; i <= max; i++) { if(flag[i]) { prime[size++] = i;//存储素数。将所有标志位依然为1的标志写入素数数组中去。 } } } void genPrime2(int max) { memset(flag, true, sizeof(flag));//首先对标签数组进行初始化,全部设为true。 int sq=sqrt((double)max)+1; //一个数 n 如果是合数,那么它的所有的因子不超过sqrt(n) int i,j, k; for(i = 2;i<=sq; i++) { if(flag[i]) for(j=2,k=max/i+1;j<k;j++) flag[i*j] = false; //所有i的j倍都不是素数 } for( i = 2 ; i <= max; i++) { if(flag[i]) { prime[size++] = i;//存储素数。将所有标志位依然为1的标志写入素数数组中去。 } } } int main() { // genPrime(MAXV); genPrime2(MAXV); //输出所有素数。 for(int i=0;i<size;i++) cout<<prime[i]<<" "; cout<<endl; system("pause"); return 0; } /* 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 请按任意键继续. . . */
运行结果:
3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 请按任意键继续. . .
【算法改进进一步设想】
就是bool型数组里面只存奇数不存偶数。如定义prime
,则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开始筛的。
这个优化也对算法复杂度的降低起到了很大的作用。
相比于一般的筛法,加入这两个优化后的筛法要高效很多。
时间有点晚了,欧拉函数早上再搞。
【未完待续】。。
相关文章推荐
- ACM 进阶学习第一课----同余相关之中国剩余定理
- ACM 进阶学习第一课----简单数学问题之同余相关
- ACM 进阶学习第一课----同余相关之中国剩余定理
- ACM 进阶学习第一课----简单数学问题之同余相关(1)
- ACM 进阶学习第一课----同余相关之欧几里得算法及其扩展(2)
- ACM学习历程20——竞赛中的简单数学问题之最大公约数、素数表、排列组合数
- 产品学习用户体验相关设计第一课
- Java学习之ACM相关基础知识
- IOS App入门开发进阶 第一课 OC语言基础学习
- ACM学习笔记之 数学问题----素数
- C++及相关面向对象进阶学习用书推荐
- 「Oracle数据库」第一课:Oracle基础相关 学习笔记
- vim进阶学习及相关配置&插件
- ACM学习进阶计划
- ACM进阶学习计划
- ACM学习总结之素数与串问题
- 关于计算机相关专业学习ACM
- ACM学习进阶计划
- LLVM每日谈之十一 编译器相关学习资料推荐
- ACM第6天 Dp进阶HDU 1421 搬寝室