zoukankan      html  css  js  c++  java
  • 第 2 章 第 2 题 找" 重数/漏数 "问题 位向量实现

    问题分析

      输入:一个包含了4 300 000 000个32位整数的文件( 其中可能有重复出现的数字 )

      输出:一个在这个文件中重复出现过了的数字

      约束:无

    解答思路

      第一章中,我们学习了如何用位向量进行一个空间代价很小的排序。在第一章的一些习题中,位向量的每一位代表的数就是该位的位序:位为1时表示存在这个数,0则表示不存在。本题也可以建立一个类似的位向量。但,本题的位向量又有所不同:因为要体现出重复的数据,因此一个数至少要两个位才能表示。故在这个新的位向量中,a ( 位表示的数 )和b ( 位序的关系 )不是a = b而是a * 2 = b1以及a*2 + 1 = b2 ( b1表示a对应的第一个位的位序,b2表示a对应的第二个位的位序 )。而且,位序00表示这两个位序对应的数不存在,位序01表示出现过一次,10表示出现过两次或者两次以上,看清楚包括两次及以上!

      建立好位向量后,就开始检索位向量,将所有“ 10 "位对应的数输出即可。

    效率分析

      空间:4 300 000 000 * 2 ( bit ) ,也就是1GB左右的样子。

      时间:O( N ) ,N这里表示输入文件中数字的个数。

    代码实现

     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 8600000000 
    15 
    16 // 模拟数组
    17 int a[1 + N/BITSPERWORD];
    18 
    19 /*
    20  * 函数功能:置位向量第i位为1
    21  * 函数说明:" i>>SHIFT "等于" i/32 " 获取到位向量目标位对应的模拟数组元素的位置
    22             " i & MASK "等于" i%32 " 获取到位向量目标位在对应的模拟数组元素中的位置
    23             " 1<<(i & MASK) " 表示一个除了要设置的那位其他位都是0的整数
    24 */
    25 void set (int i) {
    26     a[i>>SHIFT] |= (1<<(i & MASK));
    27 }
    28 
    29 /*
    30  * 函数功能:置位向量第i位为0
    31  * 函数说明:" i>>SHIFT "等于" i/32 " 获取到位向量目标位对应的模拟数组元素的位置
    32             " i & MASK "等于" i%32 " 获取到位向量目标位在对应的模拟数组元素中的位置
    33             " 1<<(i & MASK) " 表示一个除了要清空的那位其他位都是0的整数
    34 */
    35 void clr (int i) {
    36     a[i>>SHIFT] &= ~(1<<(i & MASK));
    37 }
    38 
    39 /*
    40  * 函数功能:获取位向量的第i位
    41  * 函数说明:" i>>SHIFT "等于" i/32 " 获取到位向量目标位对应的模拟数组元素的位置
    42             " i & MASK "等于" i%32 " 获取到位向量目标位在对应的模拟数组元素中的位置
    43             " 1<<(i & MASK) " 表示一个除了要获取的那位其他位都是0的整数
    44 */
    45 int tst (int i) {
    46     return a[i>>SHIFT] & (1<<(i & MASK));
    47 }
    48 
    49 
    50 int main()
    51 {
    52     /*
    53      * 清空位向量
    54     */
    55     for (int i=0; i < N; i++) {
    56         clr(i);
    57     }
    58 
    59     /*
    60      * 打开待处理文件
    61     */
    62     string filename;
    63     cout << "输入数据文件名( 当前目录下 ):";
    64     cin >> filename;
    65     
    66     fstream io;
    67     io.open(filename.c_str());
    68     if (!io) {
    69         cout << "打开文件失败" << endl;
    70     return 1;
    71     }
    72 
    73     /*
    74      * 将数据导入位向量
    75     */
    76     int num;
    77 
    78     while (io >> num) {
    79         if ((tst(2*num) == 0) && (tst(2*num + 1) == 0)) {
    80             set(2*num+1);
    81         }
    82         else if ((tst(2*num) == 0) && (tst(2*num + 1) != 0)) {
    83             clr (2*num);
    84             clr (2*num+1);
    85             set (2*num);
    86         }
    87     }
    88 
    89     /*
    90      * 输出结果
    91     */
    92     for (int i=0; i < 430000000; i++) {
    93         if ((tst(2*i)!=0) && (tst(2*i + 1)==0))
    94             cout << i << endl;
    95     }
    96             
    97     return 0;
    98 } 

    运行测试

      遗憾的是,运行出错了:

      分析了很久,程序中实在找不到问题,只得把这个问题拿去和朋友讨论。所幸某人点出了问题症结所在,令我茅塞顿开:C/C++中,数组在内存空间是连续存放的,而上述代码中的位向量数组要占用大约1GB的连续的空间,而我电脑内存本身才2G,系统分配不了这么大的连续空间给我。于是系统只得报错。另一人更犀利,他指出“ 题目不是只让你找一个重复的数吗?既然找一个就可以了,你何必用两个位来表示一个数呢?”。听到这个让我吐血。。。但,即便一位表示一数,也要用500多MB连续内存空间啊,很多机器都吃不消的。

      为了解决这个问题,我必须想到一种内存消耗更小的算法,而不是去电脑城买内存条。

      

  • 相关阅读:
    Outlook 2003 最小化到系统托盘方法 [转]
    Sql Server 得到当月第一天
    禁止用户对系统数据库表的SELECT权限
    解决IE二级链接无法打开故障
    服务器安全设置全攻略
    使用TSQL脚本在SQL Server创建角色,并给角色赋予相应权限
    Redis内部阻塞式操作有哪些?
    UML和OO
    PetShop 4 详解(转载)
    Blog开通了
  • 原文地址:https://www.cnblogs.com/scut-fm/p/3329546.html
Copyright © 2011-2022 走看看