编程珠玑之第二章questionA: 40亿个随机排列整数问题

问题描述:

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] = { 1194618};
    std::vector< int> myvecints(myints, myints +  sizeof(myints) /  sizeof( int) );

    find_missing_integers(myvecints,  20);
     return;
}

int main()
{
    print_missing_integers();
     return  0;
}

输出结果如下:




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值