原文地址:http://blog.csdn.net/morewindows/article/details/7354571
(加下划线部分,是后加内容,非原文内容)
这里不详细介绍筛素数法,本文着重对筛素数法所使用的素数表进行优化来减小其空间占用。要压缩素数表的空间占用,可以使用位操作。下面是用筛素数法计算100以内的素数示例代码(这种筛素数的方法很朴素,会多次重复访问素数表中的同一数据,改进方法请参看《改进的筛素数方法》一文):
#include <stdio.h> #include <memory.h>
const int MAXN = 100; bool flag[MAXN]; int primes[MAXN / 3 + 1], pi;
//对每个素数,它的倍数必定不是素数。 //有很多重复访问,如flag[10]会在访问flag[2]和flag[5]时各访问一次 void GetPrime_1() { int i, j; pi = 0; memset(flag, false, sizeof(flag));
for (i = 2; i < MAXN; i++) if (!flag[i]) { primes[pi++] = i; for (j = i; j < MAXN; j += i) flag[j] = true; } }
void PrintfArray() { for (int i = 0; i < pi; i++) printf("%d ", primes[i]); putchar('\n'); }
int main() { printf("用筛素数法求100以内的素数\n");
GetPrime_1(); PrintfArray();
return 0; }
运行结果如下:
上面程序是用bool数组来作标记的,bool型数据占1个字节(8位),因此用位操作来压缩下空间占用将会使空间的占用减少八分之七。
下面考虑下如何在数组中对指定位置置1,先考虑如何对一个整数在指定位置上置1。对于一个整数可以通过将1向左移位后与其相或来达到在指定位上置1的效果,代码如下所示:
//在一个数指定位上置1 int j = 0; j |= 1 << 10; printf("%d\n", j);
同样,可以将1向左移位后与原数相与来判断指定位上是0还是1(也可以将原数右移若干位再与1相与)。
//判断指定位上是0还是1 int j = 1 << 10; if ((j & (1 << 10)) != 0) printf("指定位上为1"); else printf("指定位上为0");
扩展到数组上,我们可以采用这种方法,因为数组在内存上也是连续分配的一段空间,完全可以“认为”是一个很长的整数。先写一份测试代码,看看如何在数组中使用位操作:
#include <stdio.h>
int main() { printf("对数组中指定位置上置位和判断该位\n"); //在数组中在指定的位置上写1 int b[5] = {0}; int i;
//在第i个位置上写1 for (i = 0; i < 40; i += 3) b[i / 32] |= (1 << (i % 32));
//输出整个bitset for (i = 0; i < 40; i++) { if ((b[i / 32] >> (i % 32)) & 1) putchar('1'); else putchar('0'); } putchar('\n');
return 0; }
运行结果如下:
可以看出该数组每3个就置成了1,证明我们上面对数组进行位操作的方法是正确的。因此可以将上面筛素数方法改成使用位操作压缩后的筛素数方法:
//使用位操作压缩后的筛素数方法 #include <stdio.h> #include <memory.h>
const int MAXN = 100; int flag[MAXN / 32]; int primes[MAXN / 3], pi;
void GetPrime_1() { int i, j; pi = 0; memset(flag, 0, sizeof(flag)); for (i = 2; i < MAXN; i++) if (!((flag[i / 32] >> (i % 32)) & 1)) { primes[pi++] = i; for (j = i; j < MAXN; j += i) flag[j / 32] |= (1 << (j % 32)); } }
void PrintfArray() { for (int i = 0; i < pi; i++) printf("%d ", primes[i]); putchar('\n'); }
int main() { printf("用位操作压缩后筛素数法求100以内的素数\n");
GetPrime_1(); PrintfArray();
return 0; }
//使用位操作压缩后的筛素数方法
这段代码中的 “int flag[MAXN / 32];” 应该修改为 “int flag[MAXN / 32 + 1];” 。否则:
for (j = i; j < MAXN; j += i)
flag[j / 32] |= (1 << (j % 32));
当j = 98时,已经越界了。在vs2008下调试时,发现pi在内存中可能会紧挨着flag数组,flag越界时篡改了pi的内容。导致输出结果错误。
同样运行结果为:
另外,还可以使用C++ STL中的bitset类来作素数表。筛素数方法在笔试面试出现的几率还是比较大的,能写出用位操作压缩后的筛素数方法无疑将会使你的代码脱颖而出,因此强烈建议读者自己亲自动手实现一遍。