问题描述:
A. 给定一个最多包含40亿个随机排列的32位整数的顺序文件,找出一个不在文件中的32位整数(在文件中至少缺失这样一个数——为什么?)。在具有足够内存的情况下,如何解决该问题?如果有几个外部的“临时”文件可用,但是仅有几百字节的内存,又该如何解决该问题?
问题解析:
1、首先要明白整型代表的范围如下:
由此看出一个无符号长整型大约表示43亿个数,所以大约有3亿个数是在此文件中不存在的,这样问题就明确了。
2、怎样生成包含40亿个随机排列的32位整数的顺序文件(假设里面数不重复)呢?
在windows系统下,RAND_MAX = 32767(0x7FFF); 我的Linux下RAND_MAX = 2147483647(0x7FFFFFFF). 在Windows下经过对rand()修改,这个范围可以增大,但由于不知道其实现算法,所以任何改变的都有可能改变输出值的概率,所以效果不是很理想,下面我在范围输出随机数范围[0, 65534],为什么不是65535? 因为我发现任何对rand()改变,其输出随机值的概率(如最大,最小值)都会有很多改变,所以没有找到理想的方法,下面在勉强接受的范围内对上面问题做一个映射。
下面利用位图技术和C++的bitset输出在[0. 65534]范围内不重复的随机数:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#include <iostream>
#include <bitset> #include <cstdio> #include <cstdlib> // srand, rand #include <ctime> // time, clock_t #include <cassert> // assert #define MAX_16UINT 65535 // 最大的16无符号整型数(2^16-1) #define MAX_RANDNUM 65000 // 最大的随机数个数 #define error( str ) fatal_error( str ) #define fatal_error( str ) fprintf( stderr, "%s\n", str ), exit( 1 ) using namespace std; /************************************************************************/ // 函数名称:bigrand // 函数目的:生成范围在[0, 65534]之间的随机整数 // 使用条件:RAND_MAX = 32767(0x7FFF) /************************************************************************/ size_t bigrand() { assert(RAND_MAX == 0x7FFF); return rand() + rand(); } bitset<MAX_16UINT> mybits; int main() { mybits.set(); // 默认所有位都置为1 printf( "程序开始........\n"); clock_t start_time = clock(); // 开始时间 FILE* iofile = fopen( "unsortfile.txt", "w"); if ( NULL == iofile){ fatal_error( "不能打开unsortfile.txt文件!\n"); } size_t record = 0; while (record != mybits.size()){ size_t index = bigrand(); // 输出值并将指定位置为0 if (mybits[index]){ fprintf(iofile, "%d\t", index); mybits[index] = 0; record++; //printf("reords = %d", record); } // 测试生成最大和最小值概率 if ( index == 0 || index == 65534){ printf( "index = %d\n", index); } } fclose(iofile); clock_t end_time = clock(); // 结束时间 float cost_time = ( float)(end_time - start_time) / CLOCKS_PER_SEC; printf( "bit位记录数:%d\n", mybits.size()); printf( "全部it位重置为0耗时:%5.3f秒\n", cost_time); return 0; } |
65535条数据都要这么长时间,跟别说40亿条数据了,所以需要大范围高效的随机数生成函数,C++11对随机数生成改进了不少,也比较复杂,也许可以在获得解答,可以查阅C++11 关于随机数的一些文档,以后有机会在研究!
解决方案:
1. 位图技术:
那么在有足够内存的情况下使用进行排序,查找缺失数就非常方便了!类似的解答可以参考第一章习题的解答:编程珠玑之第一章:开篇(习题)泛览 的习题2解答方案!
2. 二分搜索:
作者给出的方法是0/1探测法下的二分搜索,在不需要对所有数据的进行排序的情况下找出一个缺失的整数,我这里在小范围内做一个实现方案,当然在如此之大(40亿)的情况下还要具体修改。
如下:这里是使用0/1探测法找出所有小于20的缺失的整数:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
#include <iostream>
#include <bitset> #include <vector> #include <cstdio> #include <cstdlib> // srand, rand #include <ctime> // time, clock_t #include <cassert> // assert using namespace std; /************************************************************************/ // 函数名称:getbit // 函数返回:0或1 // 函数目的:找出value的pos比特位的值 // 使用条件:value范围是[0, 2^32-1] /************************************************************************/ bool getbit(int32_t value, int32_t pos) { assert(pos >= 0 && pos < 32); int32_t mask = 1; // 表示二进制掩码 int32_t value_32 = value; return ((value_32 >> pos & mask) == mask); } /************************************************************************/ // 函数名称:find_missing_integers // 函数返回:无 // 函数目的:找出小于max_counts(不包括)的所有缺失的整数 // 使用条件:vecints里面的数不重复, pos=0时,vetints里面最大值小于max_counts /************************************************************************/ bitset< 32> bit_missing_integer; void find_missing_integers( const vector<int32_t>& vecints, const int32_t max_counts, int32_t pos= 0) { // 计算相应bit位的0,1计数 int32_t counts_0 = 0, counts_1 = 0; counts_0 = ceil( 1. 0 * max_counts / 2 ); // ceil向上取整 counts_1 = max_counts - counts_0; vector<int32_t> vecints_0, vecints_1; for (size_t i = 0; i != vecints.size(); ++i) { if (!getbit(vecints[i], pos)) { vecints_0.push_back(vecints[i]); } else { vecints_1.push_back(vecints[i]); } } // 第pos位缺1的情况 if (counts_1 - vecints_1.size() > 0){ bit_missing_integer[pos] = 1; find_missing_integers(vecints_1, counts_1, pos + 1); } // 第pos位缺0的情况 if (counts_0 - vecints_0.size() > 0){ bit_missing_integer[pos] = 0; if (counts_0 == 1 && counts_1 == 0){ // 临界位 printf( "%ld\t", bit_missing_integer.to_ulong()); return; } else { // 递归调用 find_missing_integers(vecints_0, counts_0, pos + 1); } } return; } void print_missing_integers() { int myints[ 5] = { 1, 19, 4, 6, 18}; std::vector< int> myvecints(myints, myints + sizeof(myints) / sizeof( int) ); find_missing_integers(myvecints, 20); return; } int main() { print_missing_integers(); return 0; } |
输出结果如下: