- 浏览: 131838 次
- 性别:
- 来自: 成都
最新评论
-
yi_chao_jiang:
你好,多谢分享,问个问题,在上传数据的时候判断文件是否有上传记 ...
断点续传和下载原理分析 -
a41606709:
为什么我的tabhost显示不出来? 怎么设置在全部页面中让他 ...
TabActivity中的Tab标签详细设置 -
Zero颴:
大神篇,思路,配图都很清晰,perfect!
Android事件模型之interceptTouchEvnet ,onTouchEvent关系正解 -
QAZ503602501:
牛死人了!!!
B-树 -
mengsina:
很牛的文档。数学功底好啊
Android Matrix理论与应用详解
我的注释都写在代码里面了,就不在赘述了!如果有任何疑问欢迎留言
参考博客:
1.位操作总结:http://blog.csdn.net/morewindows/article/details/7354571
2.找素数算法总结:http://blog.csdn.net/hexiios/article/details/4400068
非常感谢上面两篇博客的仁兄,致谢!
尤其是读了位操作的那篇文章,写的非常好,希望各位都可以一次尝试哈,没准给你的程序增加一个亮点
2的总结也很好,我只是在它的基础上在详细的给了些注释,如果有不懂的地方就好理解了!
1.基础补习
什么是合数?什么是素数?百度一下你就知道
有一个重要的性质:任何一个合数一定可以表示成若干个素数之积。
如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3。也就是说,合数N一定是小于N的某个(或若干)素数的整数倍,反之,如果N不是任何比它小的素数的倍数,则N必然是素数。
这个性质就决定了下面的算法基本思想
2.检查是否是素数(这个是从基本概念出发的检测算法)
主要的改进就是检测的范围缩小从0---sqrt(n)
bool isPrime(int n){ if(n ==0 || n==1) return 0; for(int i=2; i<=sqrt(n); i++){ if(n%i == 0){ cout<<i<<endl; return 0; } } return 1; }
3.经典素数求解算法
它对每个数的检测就是利用2的方法
/** *经典素数求解算法 *prime[0]中保存素数总个数 */ int* getPrime(){ int prime[LEN] = {0};//判断是否为素数,初始都为素数 prime[0] = 0; //保存素数的总个数 for(int n=2; n<LEN; n++){ bool flag = 1; //对每个数A都按照:2到sqrt(A)依次除,看是否整除,如果能则不是素数 //这样是很低效的m*m<=n也可写成m<=sqrt(n) //注意:必须有m*m<=n 中的“=” for(int m=2; m*m<=n; m++){ //如果能整除,不是素数 if(n%m == 0){ flag = 0; break; } } if(flag){ prime[0]++; prime[prime[0]] = n; } } cout<<"\ncount:"<<prime[0]<<endl; return prime; }
4.经典算法改进版
其主要的改进的地方就是,检测的思想,前面的检测方法就是2到sqrt(A)依次除,看是否整除
改进的就不同了,它利用了合数的性质,利用已经找到的素数来除,这样大大的减少了需要整除的次数,这个改进不错!
/** *它是上面经典算法改进版 *这里prime数组中存的是素数,prime[0]中保存素数总个数 *获取0-n的所有素数 */ int* getPrime1(){ int prime[LEN] = {0};//判断是否为素数,初始都为素数 int i, n, flag; prime[0] = 0; //保存素数的总个数 for (n =2 ; n < LEN; n++) { flag = 1; //判断当前n是否被"已经找出的素数"整除(如果能,说明不是素数,根据合数性质) //使用条件prime[i] * prime[i] <= n,就是判断一个数A是否是素数的时候,不需要 //从2-A的所有数都去看是否能整除,只需要看2-sqrt(A)即可,即最终整除位置为sqrt(A),见上面isPrime(int n) //这里是用prime[i]去整除,故而只需要prime[i]*prime[i]<=n也可写成prime[i]<=sqrt(n) for (i = 1; i <= prime[0] && prime[i] * prime[i] <= n; i++) //如果不是素数,break,flag=0 if (n % prime[i] == 0) { flag = 0; break; } //flag=1,则说明是素数,存储 if (flag) { prime[0]++;//素数个数 prime[prime[0]] = n;//将确定了的素数放入数组 } } cout<<"\ncount:"<<prime[0]<<endl; return prime; }
5.厄拉多塞筛算法
所谓厄拉多塞筛算法就是:利用一个空间换时间的思想,利用一个辅助数组来存储素数的位置,然后利用的是筛选法,筛选出素数的倍数
那你就会发出一个疑问?只是筛选出素数的倍数,那是不是还有遗漏啊?这个就看看1的性质吧,即所有合数,都可以分解成若干个素数。
*首先,将所有的数组元素设为0,表示没有已知的非素数。
*然后,将已知为非素数(即为已知素数的倍数)的索引对应的数组元素设置为1。
*如果将所有较小素数的倍数都设置为1之后,a[i]仍然保持为0,则可判断它是所找的素数。
/** *厄拉多塞筛算法:prime[0]中保存素数总个数 */ int* getPrime2(){ int count = 0; int primes[LEN / 3] = {0}, pi=1; int flag[LEN] = {0};//判断是否为素数,初始都为素数 //0,1不是素数 flag[0]=1; flag[1]=1; for(int i=2; i<LEN; i++){ //倍数必然不是素数,把不是素数的都筛选出来 if(!flag[i]){ primes[pi++] = i; // 注意:这里只用素数来筛,因为合数筛时肯定被素数筛过了,所以没必要. //为什么?因为合数的性质:任何一个合数一定可以表示成若干个素数之积。如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3 //即所有合数,都可以分解成若干个素数。 for(int j=i; j<LEN; j+=i){ //为什么9624会出现在素数中? //if(j == 9624) cout<<"-----"<<i<<" "<<j<<endl; flag[j]=1; count++; } } } //从count可以看出,对于一个位置重复访问了许多次,可以优化 cout<<"访问数组总数:"<<count<<endl; primes[0] = pi - 1; cout<<"count:"<<primes[0]<<endl; return primes; }
6,厄拉多塞筛算法改进版
由于对于5来说,最大的缺憾就是利用了辅助数组,那么我们可以减少空间,反正都只是存0,1,为什么不用位来存储呢?这样就大大的减少了空间的浪费了!
首先看看一个为操作的事例:
/** *用于位测试 * */ #include <iostream> #include <cstdio> using namespace std; int main(){ //在数组中在指定的位置上写1 int b[5] = {0}; //占用连续的20个字节空间(每个int占用4字节),每个字节8位(1Byte=8bit) cout<<sizeof(b)<<" "<<sizeof(int)<<endl; int i; //在第i个位置上写1(一共有20*8=160位,现在之使用了40位) for (i = 0; i < 40; i += 3) //每int占用4字节即32位,当b[0]的32位用完,就b[1] b[i / 32] |= (1 << (i % 32)); //输出整个bitset for (i = 0; i < 40; i++) { //将数组依次右移一位,然后和1求与,判断该位是0还是1 if ((b[i / 32] >> (i % 32)) & 1) putchar('1'); else putchar('0'); } putchar('\n'); return 0; return 0; }
在下面的算法中就是利用了上面的操作,只要去看看我第一个链接,里面更详细!
/** *厄拉多塞筛算法改进版:prime[0]中保存素数总个数 */ int* getPrime3(){ int primes[LEN / 3] = {0}, pi=1; //实际素数的个数最多就是LEN / 3,故而没用LEN //用位替代int prime[LEN] = {0};//判断是否为素数,初始都为素数 int flag[LEN/32] = {0};//每个int是4字节=32bit(32位)可以存储32个0,1,故而只需要LEN/32个长度的数组即可 for(int i=2; i<LEN; i++){ //倍数必然不是素数,把不是素数的都筛选出来 if(!((flag[i/32]>>(i%32))&1)){ primes[pi++] = i; for(int j=i; j<LEN; j+=i){ //将第j位设置为1,表示不是素数 flag[j/32] |= (1<<(j%32)); } } } primes[0] = pi - 1; cout<<"count:"<<primes[0]<<endl; return primes; }
7.时间复杂度分析
10万个数:这个数来测试有些小,可以大些,这样效果比较明显
时间花费依次:62ms, 15ms, 0ms, 0ms
当然最好两个是0ms这个大些才能看出来,非常明显的效果提升
8.所有代码
/** *说明: *有一个重要的性质:任何一个合数一定可以表示成若干个素数之积。 *如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3。也就是说, *合数N一定是小于N的某个(或若干)素数的整数倍,反之,如果N不是任何比它小的素数的倍数, *则N必然是素数。 /** * Author: Blakequ@gmail.com * Data : 2012-06-02 22:20 * * */ #include <iostream> #include <cmath> #include <ctime> const int LEN = 100000; using namespace std; //检查n是不是素数 bool isPrime(int n){ if(n ==0 || n==1) return 0; for(int i=2; i<=sqrt(n); i++){ if(n%i == 0){ cout<<i<<endl; return 0; } } return 1; } /** *经典素数求解算法 *prime[0]中保存素数总个数 */ int* getPrime(){ int prime[LEN] = {0};//判断是否为素数,初始都为素数 prime[0] = 0; //保存素数的总个数 for(int n=2; n<LEN; n++){ bool flag = 1; //对每个数A都按照:2到sqrt(A)依次除,看是否整除,如果能则不是素数 //这样是很低效的m*m<=n也可写成m<=sqrt(n) //注意:必须有m*m<=n 中的“=” for(int m=2; m*m<=n; m++){ //如果能整除,不是素数 if(n%m == 0){ flag = 0; break; } } if(flag){ prime[0]++; prime[prime[0]] = n; } } cout<<"\ncount:"<<prime[0]<<endl; return prime; } /** *它是上面经典算法改进版 *这里prime数组中存的是素数,prime[0]中保存素数总个数 *获取0-n的所有素数 */ int* getPrime1(){ int prime[LEN] = {0};//判断是否为素数,初始都为素数 int i, n, flag; prime[0] = 0; //保存素数的总个数 for (n =2 ; n < LEN; n++) { flag = 1; //判断当前n是否被"已经找出的素数"整除(如果能,说明不是素数,根据合数性质) //使用条件prime[i] * prime[i] <= n,就是判断一个数A是否是素数的时候,不需要 //从2-A的所有数都去看是否能整除,只需要看2-sqrt(A)即可,即最终整除位置为sqrt(A),见上面isPrime(int n) //这里是用prime[i]去整除,故而只需要prime[i]*prime[i]<=n也可写成prime[i]<=sqrt(n) for (i = 1; i <= prime[0] && prime[i] * prime[i] <= n; i++) //如果不是素数,break,flag=0 if (n % prime[i] == 0) { flag = 0; break; } //flag=1,则说明是素数,存储 if (flag) { prime[0]++;//素数个数 prime[prime[0]] = n;//将确定了的素数放入数组 } } cout<<"\ncount:"<<prime[0]<<endl; return prime; } //------------------------------下面两个算法是典型的以空间换时间,不过空间改进即可--------------------------------------------------- /** *厄拉多塞筛算法:prime[0]中保存素数总个数 *首先,将所有的数组元素设为0,表示没有已知的非素数。 *然后将已知为非素数(即为已知素数的倍数)的索引对应的数组元素设置为1。 *如果将所有较小素数的倍数都设置为1之后,a[i]仍然保持为0, *则可判断它是所找的素数。 */ int* getPrime2(){ int count = 0; int primes[LEN / 3] = {0}, pi=1; int flag[LEN] = {0};//判断是否为素数,初始都为素数 //0,1不是素数 flag[0]=1; flag[1]=1; for(int i=2; i<LEN; i++){ //倍数必然不是素数,把不是素数的都筛选出来 if(!flag[i]){ primes[pi++] = i; // 注意:这里只用素数来筛,因为合数筛时肯定被素数筛过了,所以没必要. //为什么?因为合数的性质:任何一个合数一定可以表示成若干个素数之积。如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3 //即所有合数,都可以分解成若干个素数。 for(int j=i; j<LEN; j+=i){ //为什么9624会出现在素数中? //if(j == 9624) cout<<"-----"<<i<<" "<<j<<endl; flag[j]=1; count++; } } } //从count可以看出,对于一个位置重复访问了许多次,可以优化 cout<<"访问数组总数:"<<count<<endl; primes[0] = pi - 1; cout<<"count:"<<primes[0]<<endl; return primes; } /** *厄拉多塞筛算法改进版:prime[0]中保存素数总个数 *使用一个数组来包含最简单的元素类型,0和1两个值, *如果我们使用位的数组,而不是使用整数的数组,则可获得更高的空间有效性 */ int* getPrime3(){ int primes[LEN / 3] = {0}, pi=1; //实际素数的个数最多就是LEN / 3,故而没用LEN //用位替代int prime[LEN] = {0};//判断是否为素数,初始都为素数 int flag[LEN/32] = {0};//每个int是4字节=32bit(32位)可以存储32个0,1,故而只需要LEN/32个长度的数组即可 for(int i=2; i<LEN; i++){ //倍数必然不是素数,把不是素数的都筛选出来 if(!((flag[i/32]>>(i%32))&1)){ primes[pi++] = i; for(int j=i; j<LEN; j+=i){ //将第j位设置为1,表示不是素数 flag[j/32] |= (1<<(j%32)); } } } primes[0] = pi - 1; cout<<"count:"<<primes[0]<<endl; return primes; } int main(){ clock_t start, finish; start = clock(); int n = 0; /* cout<<"输入判断的数:"<<endl; cin>>n; if(isPrime(n)){ cout<<"是素数"<<endl; }else{ cout<<"不是素数"<<endl; } */ //-------------------------方法1(经典算法62ms)---------------------------- //getPrime(); /* int *a = getPrime(); for(int i=1; i<LEN; i++){ if(a[i]==0) break; cout<<a[i]<<" "; } */ //-------------------------方法1(经典算法改进15ms)---------------------------- //getPrime1(); /* int *a = getPrime1(); for(int i=1; i<LEN; i++){ if(a[i]==0) break; cout<<a[i]<<" "; } */ //----------------------------方法3(厄拉多塞筛算法<1ms)---------------------------- //getPrime2(); /* int *a = getPrime2(); for(int i=1; i<LEN; i++){ if(a[i]==0) break; cout<<a[i]<<" "; } */ //----------------------------方法3(厄拉多塞筛算法改进<1ms)---------------------------- //getPrime3(); int *a = getPrime3(); for(int i=1; i<LEN; i++){ if(a[i]==0) break; cout<<a[i]<<" "; } finish = clock(); cout<< "\nCost Time: " << finish - start << " ms"; return 0; }
发表评论
-
排序方法总结
2012-08-12 11:34 1178这里面包含了所有常见的排序操作 1.性能等比较分析 2 ... -
常见字符串操作大全
2012-08-12 11:34 14631.常见的字符串操作 如计算长度,求子串等都是考验基本功的, ... -
KMP算法解析
2012-08-12 11:35 2845一.理论基础 1.什么 ... -
二叉堆的实现
2012-08-12 11:35 12121.堆的概念 这里只需要注意两点: a.堆的存储方式:就是 ... -
set和map的简单实现
2012-08-10 11:35 13211.Set的简单实现 set是利用二叉查找树来实现的,而且为 ... -
红黑树的插入总结
2012-08-10 11:25 14091.红黑树 这个在july的博客中有详尽的说明,我就不在赘述 ... -
B-树实现
2012-08-10 11:03 16281.什么是B-树? 这个在我的前一篇博客中已经详细的阐释过: ... -
平衡二叉树
2012-08-10 10:39 28601.问题描述 什么是平衡 ... -
二叉排序树
2012-08-10 10:25 14871.基本概念 二叉排 ... -
B-树
2012-07-04 22:48 56771.B-树的概念 是一种多 ... -
构造哈夫曼树
2012-07-04 10:40 12331.算法说明 就是建造哈夫曼树树,从而使得构造出的树带权路径 ... -
线索二叉树
2012-07-04 09:20 12761.算法描述 就是简单的线索二叉树的建立,遍历,查找等基本操 ... -
二叉树基本操作大全
2012-07-03 18:22 26081.二叉树的基本操作 这里我有一个疑问: 在使用构造 ... -
多种队列的实现
2012-06-29 10:09 15621.算法描述 a.数据结构与算法(Mark Allen We ... -
栈的各种实现
2012-06-28 16:34 11091.算法描述 a.实现二个栈,在一个数组里面,除非没有任何空 ... -
中缀表达式转换为后缀
2012-06-28 11:10 18371.算法描述 例如a+b*c这是常见的中缀表达式,但是 ... -
后缀表达式的值
2012-06-27 16:33 13361.算法描述 计算后缀表达式的值 2.事例 如:( ... -
Josephus问题
2012-06-21 15:30 10021.算法描述 简单的游戏:有N个人坐成一圈,编号1-N、从编 ... -
关于最长递增子序列的实际应用--动态规划
2012-06-07 11:35 1837参考链接: a.http://www.programfan. ... -
线性查找二维数组
2012-06-05 17:23 10311.算法描述 有序(行有序,列有序,且每行从左至右递增 ...
相关推荐
Python 编程 -100 以内素数几种编程求解方法 Python 编程语言提供了多种方法来解决 100 以内的素数问题。下面我们将逐步讲解每种方法。 方法一:简单遍历 在第一个方法中,我们使用 for 循环来遍历从 2 到 100 的...
`MATLAB实现改进的欧拉法求解常微分方程组`可能包括了欧拉方法的变种,如中点法、四阶龙格-库塔法等。这些方法通过离散化时间和空间,将连续的微分方程转化为一系列的代数方程,然后逐步求解。MATLAB的`ode45`函数是...
### 知识点详解 #### 一、程序概述 该段C语言代码旨在实现一个功能:求解用户指定区间内的所有素数,并将其输出。...尽管存在一定的局限性和改进空间,但它作为学习素数求解算法的一个实例仍然非常有价值。
这种方法会逐步移除非素数,直到筛选出所有素数。 在提供的压缩包文件名称列表中,虽然没有直接与素数相关的文件,但它们包含了其他MATLAB编程的相关主题,如: 1. "MATLAB求解拟合圆的圆心和半径"涉及数据分析和...
这种问题通常涉及到算法的设计,如埃拉托斯特尼筛法,这是一种经典的求解素数的方法,通过逐步排除非素数来找到所有小于特定数的素数。 标签"prime"进一步确认了主题的焦点,即素数相关的知识。 压缩包中的子文件...
方法2通过数学定理减少了判断次数,而方法3通过逐步筛选出非质数来达到优化目的。在实际应用中,可以根据具体需求和数值范围选择合适的方法。另外,由于计算机技术的限制,提供的代码片段可能出现扫描错误或遗漏,但...
- 使用多项式的导数来近似根的位置,逐步改进估计值直至满足精度要求。 #### 十一、几何 **1. 多边形** 问题描述:多边形的各种属性和操作。 算法思路: - 多边形的表示和处理涉及点的坐标、边的定义、内部区域...
例如,当求解100以内的素数时,如果i值等于78,内层循环的判断范围不需要是2至100,而应该是2至77。通过这种优化,可以减少无效计算,提高程序效率。 进一步的优化可以在时间复杂度上下功夫。通过增加特定条件的...
N-S图(Nassi-Shneiderman图),又称为盒子图或结构化框图,是一种改进后的流程图表示方法,其目的是提高可读性和清晰度,避免传统流程图中存在的复杂跳转等问题。 ### 二、示例分析 #### 示例1:求解二次方程 \...
接着,教学过程应遵循知识的逻辑顺序,从基础的倍数和因数出发,逐步引导学生理解公因数和最大公因数,然后再过渡到公倍数和最小公倍数。在这个过程中,教师应强调概念间的关联性,比如倍数是因数的扩展,公倍数是多...
通过不断选取未确定最短路径中距离源点最近的顶点,并更新其邻居的距离来逐步构建最短路径树。 - **DIJKSTRA O(E*logE)**:与上面的算法相比,使用了优先队列来优化选择过程,将时间复杂度降低至O(E*logE)。 - **...
递归方法是求解排列组合问题的一种通用策略,通过逐步减小问题规模直至基本情况,从而解决问题。 **类循环排列** 类循环排列是指排列中元素的顺序具有循环性质,通常需要考虑到首尾相连的情况。 **全排列** ...
算法包括将方程变形,逐步求解未知数的值。对于任意形式的二元一次方程组,都可以按照相同的过程进行操作。 2. **判断质数**:算法首先检查输入的整数是否等于2,因为2是唯一的偶数质数。如果大于2,则逐个检查2到...
- 求解线性同余方程组可以通过扩展欧几里得算法进行,先解单个模的线性同余方程,然后逐步解决整个系统。 11. **数组中的缺失数**: - 对于有序数组,可以利用中位数性质快速找到缺失的数;对于无序数组,遍历一...
- 通过多次应用梯形法则并逐步减小步长来提高积分精度。 **5.2 多项式求根(牛顿法)** - 牛顿法是求解多项式根的一个迭代方法。 - 适用于寻找实数根的情况。 **5.3 周期性方程(追赶法)** - 追赶法适用于求解周期性...
谭浩强教授在第二章中通过几个具体例子,如计算阶乘、筛选高分学生、判断闰年、求解级数和判断素数,详细展示了如何设计和表示算法。这些例子涵盖了简单的数值运算和逻辑判断,帮助读者理解算法的设计思路和表示方法...
Prim算法是一种用于求解无向图最小生成树的经典算法,通过贪心策略逐步添加边来构建最小生成树。 **2. Dijkstra算法求单源最短路径** Dijkstra算法用于求解带有非负权重的图中单源最短路径问题。 **3. Bellman-...
- **实现方法**: 通过追赶算法逐步求解。 #### 十一、几何 **1. 多边形/多边形切割/浮点函数/几何公式/面积/球面/三角形/三维几何/凸包(Graham)/网格(Pick)/圆/整数函数** - **定义**: 包括多边形的属性、计算...