素数筛选法在很多大牛的博客中都有帖子,有的甚至给出3种方法。。。本文只是给我自己和能看到这篇文章的人分享一下我个人理解到现在的素数筛选法。
素数筛选法常常的问题背景是给出一个非负整数n,求小于(等于)n的非负整数内有几个素数。
下面先给出素数筛选法的原理:开出一个大小为n的bool型数组arr,全置为false。此时用数组下标(0 to n-1)来代表小于等于n的全部非负整数。而其中某个数值arr[i]代表这个数是否为合数,即一开始我们假设所有数是素数。不做任何优化的素数筛选,第一层循环index == 2 to n -1。第二层不断倍增index(index*2 index*3...),这样新得到的下标一定是合数,将其内容置true。这里有三个注意的地方,一是注意下标不要越界,二要注意第一层循环要判断当前位置数组的内容,如果已经被置为true,证明有更小的index1 * j == index,则index倍增的结果都可以由index1倍增得到,此时再去倍增index是徒劳的。三是0和1既不是合数也不是素数本身不参与倍增和个数统计。为了方便理解,我从LEETCODE上一道有关素数筛选的题目搞到一张解释素数筛选过程的图如下
下面给出以c++实现的代码
int countPrimes(int n) {
vector<bool> res(n, false);
for(int i = 2;i < n;++i){
if(!res[i]){
for(int j = 2;i*j < n;++j){
res[i*j] = true;
}
}
}
int count = 0;
for(int i = 2;i < n;++i){
if(!res[i])
++count;
}
return count;
}
这里可以做一些优化,全部是围绕有且只有一个偶素数2.其他偶数可以不参与筛选和最后的素数个数统计,代码如下:
int countPrimes(int n) {
vector < bool > res(n, false);
for (int i = 3; i < n; i += 2) {//从3开始筛选,i+=2保证了只关心奇数
if (!res[i]) {
for (int j = 3; i * j < n; j += 2) {//倍增的时候也跳过2及2的倍数,保证倍增的结果也是奇数
res[i * j] = true;
}
}
}
int count = n >= 3 ? 1 : 0;//统计时根据问题规模决定count中包不包括2
for (int i = 3; i < n; i += 2) {//这里统计也只关心奇数
if (!res[i])++count;
}
return count;
}
这就是我想分享的结果啦。。。优化的代码跑leetcode测试用例时常减少了一半还多。。。开心
开帖方便自己随时回炉。如果对大家有帮助就更好了,也不是什么大神,只能搞搞这种小问题。。。