From: http://blog.csdn.net/dinosoft/article/details/5829550

素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功。

基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2 。。N^(0.5) ,看看能否整除N。

如果需要判断的次数较多,则先用下面介绍的办法预处理。

一般的线性筛法

首先先介绍一般的线性筛法求素数

[cpp] view plaincopyprint?
  1. void make_prime()  {
  2. memset(prime, 1, sizeof(prime));
  3. prime[0]=false;
  4. prime[1]=false;
  5. int N=31700;
  6. for (int i=2;  i<N;  i++)
  7. if (prime[i]) {
  8. primes[++cnt ]=i;
  9. for (int k=i*i; k<N; k+=i)
  10. prime[k]=false;
  11. }
  12. return;
  13. }

这种方法比较好理解,初始时,假设全部都是素数,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数(注意上面的 i*i ,  比 i*2 要快点 ),把这些合数都筛掉,即算法名字的由来。

但仔细分析能发现,这种方法会造成重复筛除合数,影响效率。比如10,在i=2的时候,k=2*15筛了一次;在i=5,k=5*6 的时候又筛了一次。所以,也就有了快速线性筛法。

快速线性筛法

快速线性筛法没有冗余,不会重复筛除一个数,所以“几乎”是线性的,虽然从代码上分析,时间复杂度并不是O(n)。先上代码

[cpp] view plaincopyprint?
  1. #include<iostream>
  2. using namespace std;
  3. const long N = 200000;
  4. long prime[N] = {0},num_prime = 0;
  5. int isNotPrime[N] = {1, 1};
  6. int main()
  7. {
  8. for(long i = 2 ; i < N ; i ++)
  9. {
  10. if(! isNotPrime[i])
  11. prime[num_prime ++]=i;
  12. //关键处1
  13. for(long j = 0 ; j < num_prime && i * prime[j] <  N ; j ++)
  14. {
  15. isNotPrime[i * prime[j]] = 1;
  16. if( !(i % prime[j] ) )  //关键处2
  17. break;
  18. }
  19. }
  20. return 0;
  21. }

首先,先明确一个条件,任何合数都能表示成一系列素数的积。

不管 i 是否是素数,都会执行到“关键处1”,

①如果 i 都是是素数的话,那简单,一个大的素数 i 乘以不大于 i 的素数,这样筛除的数跟之前的是不会重复的。筛出的数都是 N=p1*p2的形式, p1,p2之间不相等

②如果 i 是合数,此时 i 可以表示成递增素数相乘 i=p1*p2*...*pn, pi都是素数(2<=i<=n),  pi<=pj  ( i<=j )

p1是最小的系数。

根据“关键处2”的定义,当p1==prime[j] 的时候,筛除就终止了,也就是说,只能筛出不大于p1的质数*i。

我们可以直观地举个例子。i=2*3*5

此时能筛除 2*i ,不能筛除 3*i

如果能筛除3*i 的话,当 i' 等于 i'=3*3*5 时,筛除2*i' 就和前面重复了。

需要证明的东西:

  1. 一个数会不会被重复筛除。
  2. 合数肯定会被干掉。

根据上面红字的条件,现在分析一个数会不会被重复筛除。

设这个数为 x=p1*p2*...*pn, pi都是素数(1<=i<=n)  ,  pi<=pj ( i<=j )

当 i = 2 时,就是上面①的情况,

当 i >2 时, 就是上面②的情况, 对于 i ,第一个能满足筛除 x 的数  y 必然为 y=p2*p3...*pn(p2可以与p1相等或不等),而且满足条件的 y 有且只有一个。所以不会重复删除。

证明合数肯定会被干掉? 用归纳法吧。

类比一个模型,比如说我们要找出 n 中2个不同的数的所有组合 { i , j } ,1<=i<=n, 1<=j<=n,

我们会这么写

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

for (j=i+1; j<=n; ++j)

{

/

}

我们取 j=i+1 便能保证组合不会重复。快速筛法大概也是这个道理,不过这里比较难理解,没那么直观。

1楼提供的方法,我整理下

//偶数显然不行,所以先去掉偶数。可以看作上面第一种的优化吧。

//不过这种方法不太直观,不太好理解。

[cpp] view plaincopyprint?
  1. 我推荐这个算法! 易于理解。 只算奇数部分,时空效率都还不错!
  2. half=SIZE/2;
  3. int sn = (int) sqrt(SIZE);
  4. for (i = 0; i < half; i++)
  5. p[i] = true;// 初始化全部奇数为素数。p[0]对应3,即p[i]对应2*i+3
  6. for (i = 0; i < sn; i++) {
  7. if(p[i])//如果 i+i+3 是素数
  8. {
  9. for(k=i+i+3, j=k*i+k+i; j < half; j+=k)
  10. // 筛法起点是 p[i]所对应素数的平方 k^2
  11. // k^2在 p 中的位置是 k*i+k+i
  12. //    下标 i         k*i+k+i
  13. //对应数值 k=i+i+3   k^2
  14. p[j]=false;
  15. }
  16. }
  17. //素数都存放在 p 数组中,p[i]=true代表 i+i+2 是素数。
  18. //举例,3是素数,按3*3,3*5,3*7...的次序筛选,因为只保存奇数,所以不用删3*4,3*6....

扩展阅读

  1. 打印质数的各种算法 http://coolshell.cn/articles/3738.html  里面有个用C++模板实现的,纯属开阔眼界,不怎么实用。
  2. 检查素数的正则表达式  http://coolshell.cn/articles/2704.html  数字n用  1111。。1 (n个1)表示,纯属坑爹。

===========================================================================

以上完整源码(a.cpp)

/*
功能:     求[0, 20000)间的所有素数, 假设内存空间足够
环境: Linux C++
编译: g++ -o a a.cpp -Wall -Os
总结: 这两种算法在性能上差距不是很大, 当N个数较少时, 还是"一般线性筛选法"速度更快,
但是当N较大时, 快速线性筛选法的优势更加明显
*/
#include <stdio.h>
#include <string.h>
#define N 20000
// 一般线性筛选法: 会出现重复筛选同一个数
void make_prime(int primes[], int& cnt)
{
bool bPrime[N];     // 质数标志数组
cnt = 0;           // 素数个数
memset(bPrime, true, sizeof(bPrime));// 假设全是素数
bPrime[0] = false;                 // 0: 非素数
bPrime[1] = false;                 // 1: 非素数
for (int i = 2; i < N; i++)
{
if (bPrime[i])                  // i是素数
{
primes[cnt++] = i;           // 将素数i保存到bPrimes[]中
// 作筛选: i的倍数都不是素数
for (int k = i * i; k < N; k += i)    // 将素数i的倍数全置为非素数标志
bPrime[k] = false;
}
}
}
// 快速线性筛选法: 不会出现重复筛选同一个数
void getPrimes(int primes[], int& cnt)
{
bool bPrime[N];     // 素数标志数组
cnt = 0;       // 素数个数
memset(bPrime, true, sizeof(bPrime));   // 假设全部为素数
bPrime[0] = false; // 0: 非素数
bPrime[1] = false; // 1: 非素数
for(int i = 2; i < N; i++)
{
if(bPrime[i])   // i是素数
primes[cnt++] = i;           // 保存素数i
// 作筛选: i与素数的乘积都不是素数
for(int j = 0; j < cnt && i * primes[j] < N; j++)
{
bPrime[i * primes[j]] = false; // 置非素数标志
if(i % primes[j] == 0)        // i为素数的倍数
break;
}
}
}
int main()
{
int primes[N];      // 保存所有素数
int cnt = 0;       // 素数个数
#if 1
make_prime(primes, cnt);    // 调用一般线性筛选法
#else
getPrimes(primes, cnt);     // 调用快速线性筛选法
#endif
for(int i = 0; i < cnt; i++)
printf("primes[%d] = %d\n", i, primes[i]);
printf("\n素数个数cnt=%d\n", cnt);
return 0;
}

求素数: 一般线性筛法 + 快速线性筛法相关推荐

  1. 经典中的经典之——筛选法求素数(埃氏筛 | 线性筛)

    题目描述 统计小于非负整数n的质数数量 浑水摸鱼之蛮力验证法 直接上代码 bool is_zen(int x) {int i = 2;while (i * i <= x) {if (x % i ...

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

    一般筛法求素数+快速线性筛法求素数 标签: 正则表达式算法优化扩展c 2010-08-22 01:28 28738人阅读 评论(8) 收藏 举报  分类: 算法学习资料(5)  版权声明:本文为博主原 ...

  3. 一般筛法和快速线性筛法求素数 求素数的一点总结

    素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功. 基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2 ..N^(0.5) ,看看能否整除N. 如果需要判断的次数较多,则先用 ...

  4. 线性筛法求素数c语言,[算法]素数筛法(埃氏筛法线性筛法)

    一.素数筛的定义 给定一个整数n,求出[1,n]之间的所有质数(素数),这样的问题为素数筛(素数的筛选问题). 二.埃氏筛法(Eratosthenes筛法) 埃氏筛法又叫做Eratosthenes筛法 ...

  5. 线性O(N)时间复杂度求素数 , 筛法

    线性O(N)时间复杂度求素数 , 筛法 1 /* 2 线性时间求出1-N 的素数 , 时间复杂度为O( N) : 3 一个合数可以表示成若干个素数的积 4 比如说 i = 6 =2 * 3 , A = ...

  6. 素数筛法求素数(线性时间)

    摘自:http://blog.csdn.net/once_hnu/article/details/6302283 逆向思维的典型应用! 1)普通方法判断素数:O(n*sqrt(n)) 2)筛法求素数: ...

  7. 数论 判断素数:普通素数判别 线性筛 二次筛法求素数 米勒拉宾素数检验

    普通的素数判断法 当我们要判断一个数字是否是素数的时候,往往会直接看这个数字模1到这个数字的根号,看有没有等于零的,从而判断这个数字是不是素数,这样做的时间复杂度为O(sqrt(n)) bool is ...

  8. 筛法快速求素数——leetcode计数质数

    在算法竞赛中经常会遇到求质数的问题,这种题目一般都是要求出一定范围内[0,n]所有的质数或者质数的个数.最直接的思路就是根据质数的定义来判定一个数是不是质数(即一个数不能被除1和它本身外的任何数整除) ...

  9. 素数筛法(传统普通、朴素筛法、埃式筛法、欧拉筛法(线性筛))

    素数筛法(普通.朴素筛法.埃式筛法.欧拉筛法) 1.题目 2.分析 3.代码 传统普通 朴素筛法 朴素筛法(6.14) 埃式筛法 埃式筛法(6.14) 欧拉筛法(线性筛) 欧拉筛法(线性筛 6.14) ...

最新文章

  1. linux环境下快速配置hadoop集群免密登录
  2. php mssql_result 255 string,PHP连接MSSQL时nvarchar字段长度被截断为255的解决方法
  3. Java是否支持默认参数值?
  4. SpringBoot中通过@Value获取自定义配置的值
  5. Html img 标签
  6. ansys用什么cpu_ANSYS图形工作站与集群配置探讨201904-1
  7. java3d创建立方体_Opengl创建几何实体——四棱锥和立方体
  8. php查询mysql返回大量数据结果集导致内存溢出的解决方法
  9. 【Json】JSONPath之fastJson与Snack3的使用介绍与区别
  10. jQuery使用示例详解
  11. html单选框换行,html – 如何防止单选按钮及其标签之间的换行符,同时仍然允许标签本身中的换行符?...
  12. Django 路由系统
  13. i7 8750h支持linux,开启游戏本六核时代!酷睿i7-8750H处理器性能实测
  14. 贝叶斯(五)贝叶斯决策
  15. 红蓝药丸的选择:大脑在缸中还是颅中?
  16. 文件源此计算机上的硬盘驱动器是什么意思,Win10传输到外部硬盘驱动器的解决方法...
  17. gazebo publish pose
  18. 常见嵌入式WEB服务器
  19. 带空格直角三角形图案
  20. offsetParent和parentElement的区别

热门文章

  1. 同一页面引入多个JS文件的编码问题
  2. 西工大java实验报告给,西工大数字集成电路实验 实验课6 加法器的设计
  3. leetcode 721. 账户合并(并查集)
  4. leetcode 452. 用最少数量的箭引爆气球(贪心算法)
  5. leetcode852. 山脉数组的峰顶索引(二分法)
  6. leetcode1536. 排布二进制网格的最少交换次数(贪心算法)
  7. leetcode89. 格雷编码
  8. 组件分页_如何创建分页组件
  9. 使用这些HTTP标头保护您的Web应用程序
  10. 如何在开源社区贡献代码_如何在15分钟内从浏览器获得您的第一个开源贡献