编程珠玑:0/1二分搜索

编程珠玑第二章问题A:

给定一个包含40亿个随机排列的顺序文件,找到一个不在文件中的32位整数,在有足够内存的情况下应该如何解决该问题?如果有几个外部的临时文件可用,但是仅有几百字节的内存,又该如何解决?

如有足够内存,完全可用第一章介绍的位图方法解决。这里关注内存不足时的解决方案。

分析:

32位整数,可表示的整数个数为2^32 = 4 294 967 296 > 40亿,因此一定有些数据不在此文件中。

设存有40亿数据的文件为F,其中每个数据均为32bit,不在该文件中的数据集合为M(j。F 中的数据和M中的数据一起构成32比特能够表示的所有数据,即|F| + |M| = 2^32。

从F中各数据的最高位比特开始,按其为0或者1分为两部分,假设分别为A、B,则A、B的大小有两种情况:
1、A和B中的数据个数相同。[由于|F| < 2^32,则|A| = |B| = |F|/2 < 2^31,]由于最初的数据文件F不包含数据M,则A、B均不包含M中的数据,即A、B中均缺失32比特能够表示的”部分数据“(即M中的数据),此时随便找一个文件,比如A,设定其为新的F,同时假设A中缺失的数据为新的M(也就是先前M中以0开头的数据,由于A 是以0开头的数据,事实上M当然也是以0开头的数据),然后按照次高位比特进行划分。
2、A和B中的数据个数不同。假设A的个数多余B中的个数,即|A| > |B|,不能确定A中是否缺失数据,因为A完全可能包含以0开头的所有数据,这样A就不缺失数据。但是可以确定的是B一定缺失数据,否则|B| = 2^31,总数为40亿(小于2^32),导致A中的数据小于2^31,进而少于于B中的数据个数,与开始处的假设矛盾。令B为新的F,B中缺失的数据位新的M(即M中以1打头的数据,这也是由于B是以1开始的)。

通过上述最高位比特的分析后,可以得到一个文件F,其数据规模不超过N/2,其中N为原F中的所有数据。同时可知有些数据不在文件F中,仅在M中。接着按第二位比特的信息对F进行上述的A、B分类,A是F中第二位比特为0的数据,B是F中第二位比特为1的数据。也能得出A、B中数据个数的信息。
1、若|A| = |B|,即A、B缺失数据,令F为A即可,继续。
2、若|A| != |B|,假设|A| > |B| ,则B缺失数据,令F为B,继续。
3、若|A| != |B|,假设|A| = 0,则可得缺失数据YYY0XXX,Y为已探索的位,X为任意位。

可以得到新的F其规模不超过N/4,继续A、B分类即可。

示例模拟:

一大堆的文字描述不好理解,下面以一组4bit数字模拟一下上面的过程:

4bit共可表示2^4 = 16个整数,假设初始文件F如下:

0  0000
1  0001
3  0011
4  0100
5  0101
6  0110
7  0111
8  1000
9  1001
11 1011
12 1100
13 1101
14 1110
15 1111

前面一列为10进制数,可以看到文件中缺失了0010(2),1010(10)。

第一次分解文件:

A文件只包含0xxx的数:(x为未探索的位)

0  0000
1  0001
3  0011
4  0100
5  0101
6  0110
7  0111

B文件只包含1xxx的数:

8  1000
9  1001
11 1011
12 1100
13 1101
14 1110
15 1111

按照上面的分析,|A|=|B|=7 < 2^3 ,都有数据缺失,随机选取A文件为新的文件F。

第二次分解文件:

A文件只包含00xx的数:

0  0000
1  0001
3  0011
B文件只包含01xx的数:

4  0100
5  0101
6  0110
7  0111

由于|A| < |B|,选取A为新的F文件。

第三次分解文件:

A文件只包含000x的数据:

0  0000
1  0001
B文件只包含001x的数据:

3  0011

由于|B| < |A|,选取B为新的F文件。

第四次分解文件:

A文件只包含0010的数据:没有数据。

B文件只包含0011的数据:0011

此时|A| < |B| ,所以缺失的数据必在A中,那么缺失的数据应该为0010,即十进制2。


代码实现:

#include <stdio.h>
#include <stdlib.h>

#define MAX_BIT   32

void file_split(FILE *srcfd, FILE *bit0fd, FILE *bit1fd, unsigned int *bit0_count, unsigned int *bit1_count, int cur_bit);

/* binary search(0/1 search). For simplicity, ignore error checking */
int main(int argc, char** argv)
{
   unsigned int   mdata = 0;
   unsigned int   bit0_count = 0;
   unsigned int   bit1_count = 0;
   unsigned int   missing_num = 0xFFFFFFFF;
   char           mdatafn[] = "mdata.txt";
   char           bit0fn[] = "bit0.txt";
   char           bit1fn[] = "bit1.txt";
   FILE          *mdatafn_ptr = NULL;
   FILE          *bit0_ptr = NULL;
   FILE          *bit1_ptr = NULL;
   int            cur_bit = MAX_BIT;

   mdatafn_ptr = fopen(mdatafn, "w+");
   
   printf("Please input one number or input EOF to stop input:\n");
   while(scanf("%u", &mdata) != EOF)
   {
      fprintf(mdatafn_ptr, "%u\n", mdata);
      printf("Please input one number or input EOF to stop input:\n");
   }
   fflush(mdatafn_ptr);
   rewind(mdatafn_ptr);

   bit0_ptr = fopen(bit0fn, "w+");
   bit1_ptr = fopen(bit1fn, "w+");
   while(cur_bit-- > 0)
   {
      bit0_count = 0;
      bit1_count = 0;
      file_split(mdatafn_ptr, bit0_ptr, bit1_ptr, &bit0_count, &bit1_count, cur_bit);
      if(bit0_count <= bit1_count) /* if |A| <= |B|, select A as new metadata */
      {
         printf("the missiong data's %d bit is 0.\n", cur_bit+1);
         missing_num &= (~(1<<cur_bit));
         
         mdatafn_ptr = fopen(bit0fn, "r+");
         bit0_ptr = fopen(mdatafn, "w+");
         bit1_ptr = fopen(bit1fn, "w+");
      }
      else /* if |B| < |A|, select B as new metadata */
      {
         printf("the missiong data's %d bit is 1.\n", cur_bit+1);
         missing_num |= (1<<cur_bit);
         mdatafn_ptr = fopen(bit1fn, "r+");
         bit0_ptr = fopen(bit0fn, "w+");
         bit1_ptr = fopen(mdatafn, "w+");
      }

      if(0 == bit0_count || 0 == bit1_count)
      {
         break;
      }
   }
   fclose(mdatafn_ptr);
   fclose(bit0_ptr);
   fclose(bit1_ptr);
   unlink(mdatafn);
   unlink(bit0fn);
   unlink(bit1fn);
   
   printf("missing num is: %u\n", missing_num);
   return 0;
}

/* get every unsigned int number from file pointed by srcfd, if it's single bit according cur_bit is 0, then write to file pointed by bit0fd or else bit1fd
   return every sub-file's number of unsigned int */
void file_split(FILE *srcfd, FILE *bit0fd, FILE *bit1fd, unsigned int *bit0_count, unsigned int *bit1_count, int cur_bit)
{
   char           mdatastr[32] = {0,};
   unsigned int   mdata = 0;

   if(NULL == srcfd || NULL == bit0fd || NULL == bit1fd || NULL == bit0_count || NULL == bit1_count)
   {
      printf("invalid input parameter\n");
      exit(-1);
   }

   while(fgets(mdatastr, 32, srcfd))
   {
      mdata = strtoul(mdatastr, NULL, 10);
      if(mdata & 1<<cur_bit)
      {
         printf("put %08X to bit_1.txt.\n", mdata);
         fprintf(bit1fd, "%u\n", mdata);
         (*bit1_count)++;
      }
      else
      {
         printf("put %08X to bit_0.txt.\n", mdata);
         fprintf(bit0fd, "%u\n", mdata);
         (*bit0_count)++;
      }
      memset(mdatastr, 0, sizeof(mdatastr));
   }
   fclose(srcfd);
   fclose(bit0fd);
   fclose(bit1fd);
}

参考:http://leengsmile.blog.163.com/blog/static/177038063201282225132321/

你可能感兴趣的:(C++,c,编程珠玑,数据结构与算法,试题)