位操作与空间压缩
本文着重对筛素数法所使用的的素数表进行优化来减小其空间占用。要压缩素数表的空间占用,可以使用位操作。下面是用筛素数法计算100以内的素数示例代码:
#include<stdio.h>
#include<memory.h>
const int MAXN = 100;
bool flag[MAXN];
int primes[MAXN / 3], 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");
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 << (1 % 32));
}
}
}
}
void PrintArray(){
for(int i = 0; i < pi; i++){
printf("%d ", primes[i]);
}
putchar('\n');
}
int main(){
printf("用位操作压缩后筛素数法求100以内的素数\n");
GetPrime_1();
PrintArray();
return 0;
}
详细参考:https://www.cnblogs.com/zhoug2020/p/4978822.html