Bitmap算法及其应用——求素数
2018-03-08 20:30
176 查看
在学习hashtable的时候,发现用于标记懒惰删除的数据结果是Bitmap,详见数据结果习题解析(c++语言)(第三版) 邓俊辉编著。理论上来说,可以用一个数组或者是std::vector来进行标记,但是Bitmap则具有更高的空间效率和时间效率。下面主要参考该书,对Bitmap进行了学习。
上述程序基本与数据结果习题解析(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()均是常数时间复杂度,并且使用了更为高效的位操作,有较高的效率。
自然数分为素数和合数,所有素数的整数倍均是合数,Eratothenes(希腊先哲)筛法就是基于上述的思想。从2开始对自然数进行遍历,将2的整数倍的数去掉,然后遍历到3, 将3的整数倍筛掉,在遍历到5(4已经被去掉了), 将5的整数倍去掉,从此往后,剩下的数均是素数。
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
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
相关文章推荐
- otsu自适应阈值分割的算法描述和opencv实现,及其在肤色检测中的应用
- RANSAC算法及其在消除错配中的应用
- Hash 算法及其应用
- Hash 算法及其应用
- 算法之递推及其应用(递推关系的建立及在信息学竞赛中的应用 安徽 高寒蕊)
- 位运算符及其应用 海量数据处理算法—Bit-Map
- 动态规划(DP)算法及其应用
- RANSAC算法及其消除错配应用
- otsu自适应阈值分割的算法描述和opencv实现,及其在肤色检测中的应用
- C++ 递归位置排列算法及其应用
- PHP-密码学算法及其应用-散列函数
- 莫队算法及其应用
- 基于局部均方差相关信息的图像去噪及其在实时磨皮美容算法中的应用。
- 一个应用实例详解卡尔曼滤波及其算法实现(通俗易懂哦)
- Bitmap 算法解释与应用
- RANSAC算法及其消除错配应用
- RANSAC算法及其消除错配应用
- TLD跟踪算法学习及其在摄像机控制中的应用
- otsu自适应阈值分割的算法描述和opencv实现,及其在肤色检测中的应用
- 一个应用实例详解卡尔曼滤波及其算法实现