第 1 章 第 5 题 空间敏感排序问题( 扩展 ) 位向量实现

问题分析

  此前实现的位向量排序需要一个有1000万位的位向量,也就是要占用大概1.2MB的存储空间。本题要求将内存需求从1.2MB限制到1MB。也就是说要用800多万位存放大约1000万个数字并完成排序( 注意是存放1000万个数字而不是表示这些数字中的某一个 )。结合实际情况分析可知,美国的电话号码不会以0或者1开头,因此0 - 1999999这个范围内的数字不用在位向量中占据位置,这样就节省了0.23 - 0.24MB的容量,满足要求。下面我们对本章第 3 题的代码进行改进,使其内存消耗严格固定在1MB之内。

代码实现

  1 #include <iostream>
  2 #include <fstream>
  3 #include <string>
  4 
  5 using namespace std;
  6 
  7 // 每个整数的位数
  8 #define BITSPERWORD 32
  9 // 每次位移量
 10 #define SHIFT 5
 11 // 取模掩码
 12 #define MASK 0x1F
 13 // 位向量的元素个数
 14 #define N 10000000
 15 #define M 2000000
 16 
 17 // #### 修改部分 ####
 18 // 模拟数组( 这次只用了800万位 )
 19 int a[1 + (N-M)/BITSPERWORD];
 20 
 21 /*
 22  * 函数功能:置位向量第i位为1
 23  * 函数说明:" i>>SHIFT "等于" i/32 " 获取到位向量目标位对应的模拟数组元素的位置
 24             " i & MASK "等于" i%32 " 获取到位向量目标位在对应的模拟数组元素中的位置
 25             " 1<<(i & MASK) " 表示一个除了要设置的那位其他位都是0的整数
 26 */
 27 void set (int i) {
 28     // #### 修改部分 ####
 29     // 将记录在位向量中对应的位置提前200万位
 30     i -= M;
 31     a[i>>SHIFT] |= (1<<(i & MASK));
 32 }
 33 
 34 /*
 35  * 函数功能:置位向量第i位为0
 36  * 函数说明:" i>>SHIFT "等于" i/32 " 获取到位向量目标位对应的模拟数组元素的位置
 37             " i & MASK "等于" i%32 " 获取到位向量目标位在对应的模拟数组元素中的位置
 38             " 1<<(i & MASK) " 表示一个除了要清空的那位其他位都是0的整数
 39 */
 40 void clr (int i) {
 41     // #### 修改部分 ####
 42     // 将记录在位向量中对应的位置提前200万位
 43     i -= M;
 44     a[i>>SHIFT] &= ~(1<<(i & MASK));
 45 }
 46 
 47 /*
 48  * 函数功能:获取位向量的第i位
 49  * 函数说明:" i>>SHIFT "等于" i/32 " 获取到位向量目标位对应的模拟数组元素的位置
 50             " i & MASK "等于" i%32 " 获取到位向量目标位在对应的模拟数组元素中的位置
 51             " 1<<(i & MASK) " 表示一个除了要获取的那位其他位都是0的整数
 52 */
 53 int tst (int i) {
 54     // #### 修改部分 ####
 55     // 将记录在位向量中对应的位置提前200万位
 56     i -= M;
 57     return a[i>>SHIFT] & (1<<(i & MASK));
 58 }
 59 
 60 
 61 int main()
 62 {
 63     /*
 64      * 清空位向量
 65     */
 66     for (int i=0; i < N; i++) {
 67         clr(i);
 68     }
 69 
 70     string filename;
 71     cout << "输入数据文件名( 当前目录下 ):";
 72     cin >> filename;
 73     
 74     fstream io;
 75     io.open(filename.c_str());
 76     if (!io) {
 77         cout << "打开文件失败" << endl;
 78     return 1;
 79     }
 80 
 81     /*
 82      * 根据数据文件记录给位向量赋值
 83      * 赋值完了以后其实已经" 完成排序了 "
 84     */
 85     int data;
 86     while (io >> data) {
 87         set(data);
 88     }
 89 
 90     io.close();
 91     io.clear();
 92     io.open(filename.c_str(), fstream::out | fstream::trunc);
 93 
 94     /*
 95      * 从位向量获取数据并写回数据文件
 96     */
 97     for (int i=0; i <N; i++) {
 98         if ( tst(i) ) {
 99             io << i << " ";
100         }
101     }
102 
103     io.close();
104 
105     return 0;
106 }

说明

  1. 如果需要限制空间,有以下三种思路:a. 分析问题本身看看是不是可以减少待处理数据 b. 优化存储结构( 参考稀疏矩阵存储方法 ) c. 牺牲时间换取空间( 参考3 )

  2. 本题给出代码执行效率和第 3 题的效率几乎一样( 很显然不做实际测试 ),这也说明了作者的那个观点 --- 很多时候获取空间未必要牺牲时间

  3. 可以采用两次排序的方法,即第一次取出前700万个记录排序,第二次取出剩下的进行排序。但由于两次读取文件,效率会降低不少。

posted on 2014-03-20 11:39  空山悟  阅读(248)  评论(0编辑  收藏  举报

导航