您的位置:首页 > 其它

Bitmap算法及其应用——求素数

2018-03-08 20:30 176 查看
        在学习hashtable的时候,发现用于标记懒惰删除的数据结果是Bitmap,详见数据结果习题解析(c++语言)(第三版) 邓俊辉编著。理论上来说,可以用一个数组或者是std::vector来进行标记,但是Bitmap则具有更高的空间效率和时间效率。下面主要参考该书,对Bitmap进行了学习。

1. 什么是Bitmap

1Byte = 8 bit,而Bitmap的每个bit位对应0或者1,代表真或假,bitmap则是一系列0与1构成的集合。如果用bool数组来代表Hashtable的懒惰删除标记,bool占一个字节,有8bit,其所占空间为Bitmap的8倍。另外,bitmap不仅仅是空间效率高,还可以在O(1)时间内,对其进行赋值和读取。

2. Bitmap的c++实现

/// @file bitmap.hpp
/// @brief declaration and implementation of bitmap class
/// @author Shengfa Zhu, 1140973730@qq.com
/// @version 1.0
/// @date 2018-03-06

#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>//header file of FILE, fopen, fread, fclose

class Bitmap {
private:
char* M;//M[] is room for bitmap N * sizeof(char)*8 bit
int N;

protected:
void init(int n) {
N = (n + 7) / 8;
M = new char
;
memset(M, 0, N);
}

public:

/// @brief constructor
Bitmap(int n = 8) {//creat bitmap with given size
init(n);
}

/// @brief overide constructor
Bitmap(char* file, int n = 8) {//creat bitmap from file
init(n);
FILE* fp = fopen(file, "r");
fread(M, sizeof(char), N, fp);
fclose(fp);
}

/// @brief deconstructor
~Bitmap(){
delete []M;
M = NULL;
}

/// @brief set kth positon to 1
void set(int k) {
expand(k);
//1 byte = 8 bit, 0x80=128, 0x07=7
// a |= b :: a = a | b
M[k >> 3] |= (0x80 >> (k & 0x07));//位操作
}

/// @brief clear kth positin of bitmap
void clear(int k) {
expand(k);
M[k >> 3] &= ~(0x80 >> (k & 0x07));
}

/// @brief test ith is true or not
bool test(int k) {
expand(k);
return M[k >> 3] & (0x80 >> (k & 0x07));
}

/// @brief expand room of M if k overflow
void expand(int k) {
if (k < 8 * N) return;
int oldN = N;
char* oldM = M;
init(2 * k);//double size
memcpy(M, oldM, oldN);//copy data to new M
delete [] oldM;//release old room
}

/// @brief convert n position to string
char* bits2string(int n) {
expand(n - 1);
char* s = new char[n + 1];
s
= '\0';
for (int i = 0; i < n; i++) {
s[i] = test(i) ? '1' : '0';
}
return s;
}

void dump(const char* file) {
FILE* fp = fopen(file, "w");
fwrite(M, sizeof(char), N, fp);
fclose(fp);
}
};

上述程序基本与数据结果习题解析(c++语言)(第三版) 邓俊辉编著的P61-62相同。其中,以字符数组来表示bitmap,在c++中char占一个字节,也就是8bit。在赋值的时候,由于无法对某个bit进行赋值操作,因此应用了位操作。
下面重点解释下下面这行程序:M[k >> 3] |= (0x80 >> (k & 0x07))取k = 10 = 000001010(二进制)为例,应当将第10个bit(0为起点)设为1,也就是第2个字符(以1为起点)中的第3个(以1为起点),而其他位保持不变。
k>>3 : 表示右移三位 k >>3 = 00000001=1, 取数组的第2个字符进行操作;
k & 0x07 : 0x07(16进制)= 7(10进制)= 00000111 (2进制),k & 0x07 = 000000010 = 2,
0
4000
x80 >>(k & 0x07) : 0x80(16进制)= 128( 10进制)= 10000000 (2进制), 0x80 >>(k & 0x07) = 00100000
再经过一个与位操作之后,就可以将对应位置上的数设为1,而不影响其他位置。
从上面程序可以看出,set() clear() test()均是常数时间复杂度,并且使用了更为高效的位操作,有较高的效率。

3. Eratothenes筛法求素数

bitmap有很多的应用,例如可以用来不重复数据的排序,下面介绍如何基于bitmap求素数。先简要介绍下一种求素数的算法Eratothenes筛法。
自然数分为素数和合数,所有素数的整数倍均是合数,Eratothenes(希腊先哲)筛法就是基于上述的思想。从2开始对自然数进行遍历,将2的整数倍的数去掉,然后遍历到3, 将3的整数倍筛掉,在遍历到5(4已经被去掉了), 将5的整数倍去掉,从此往后,剩下的数均是素数。

4. 基于Bitmap类 Eratothenes筛法的实现

可以基于Bitmap方便地实现Eratothenes算法求得不超过n的所有素数,首先0和1不是素数。从2开始遍历到n,将素数的整数倍所对应的bit位置设为1。遍历完成之后,所有bit位是0的数就是不超过n的所有素数。void Eratothenes(const int& n) {
Bitmap B(n);
B.set(0);//0 is not prime number
B.set(1);//1 is not prime number
for(int i = 2; i < n; i++) {
if(!B.test(i))
for(int j = i * i; j < n; j += i)
B.set(j);
}
//print to monitor
for(int i = 2; i < n; i++) {
if(!B.test(i))
std::cout << i << "\t";
}
std::cout << std::endl;
}以上内容,大都参考邓俊辉老师编写的教材,将其记录下来方便自己自学,欢迎与我交流,1140973730@qq.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: