首页 > 算法讨论, 编程 > 由“中位数”想起的—-分治 or 计数排序?

由“中位数”想起的—-分治 or 计数排序?

2010年8月28日 小卢 发表评论 阅读评论

前天在网上看到一个题目,说的是求中位数,但是这个中位数,不是我们所讨论的那样简单。看题目如下:

在32位机器中,int占4字节,存在文件中的4T个数中找出第2T大的数,内存只有2G。请列出解法。

这个题目,其中有个问题有些复杂,不过肯定是有解法的。其中,4T个数,每个数占用4个字节,那么就应该是16TB的内存才能存的下,这样算来,是不能在内存里面处理全部数据了,其实如果真的想不到好的办法,可以用最朴素(也可以说是最笨)的方法,对文件进行外排(归并),就可以求出来了。

看到这个问题,我首先想的到的是用分治法。分治是肯定可以解决这个问题的。

解法1 分治法

依次读取数据,按每一个数据的最高位分别写到文件(bit_32_1、bit_32_0)里面去,并同时记录最高位为1的个数,这样就可以找到第2T个数是在bit_32_1 or bit_32_0中,依次递归下去,就能找到第2T个数是多少。

我们考虑到,数据比较大,而且是放到文件中的,如果用这种方法,就多次(<=32次)读取文件,时间复杂度应该是O(32*N)。有没有更好的办法呢?答案肯定是有的。我们在群里讨论了这个问题。提出了一个基于计数的思想。

解法2 计数分治

既然全部的数据在内存中存放不下,那么我就考虑存放两个计数桶,分别求出第2T个数的高16位和低16位,这样就不把问题解决了吗。而且使用内存较小。64K x 8 x 2 = 1MBytes下面是我写的一个伪代码,可能有些疏忽的地方,但是思想肯定是对的啦!呵呵

long long High[1<<16];
long long Low[1<<16];
long long x = 0 , i = 0;
int  Number = 0 ;
while(x<= 4T)
{
     data = read();
     High[x>>16]++;//记录高16位–yqshare
     Low[x&0xFFFF]++;  //记录低16位–yqshare
}
x = 0 ;
while(x>=2T)
{
     x += High[i++];//累计大小–yqshare
}
Number = (i–)<<16 ;
x = 0 ;
i = 0 ;
while(x>=2T)
{
     x += Low[i++];
}
Number |= (i–)&0xFFFF;//添加上低16位–yqshare

上面是错误的解法,Harvey指出来以后,我思考了下,发现自己图略了一个问题。然后修改了一下算法。

long long High[1<<16] = {0} ;
long long Low[1<<16]= {0} ;
long long x = 0 , i = 0 ,HighCount;
int  Number = 0 ;
int HighNumber , LowNumber;;
while(x<= 4T)
{
   data = read();
   High[data>>16]++;//记录高16位–yqshare
   x++;
}
x = 0 ;
i = 0;
while(x>=2T)
{
   x += High[i++];//累计大小–yqshare
}
HighNumber = (–i)<<16 ;
HighCount = i ;
while(HighCount>=High[i])
{
   i++ ;//计算出小于2T数的个数–yqshare
}
//这里可以用堆的思想,也可以用桶的思想。
x = 0 ;
while(x<= 4T)
{
   data = read();
   if(data&0XFFFF0000 == HighNumber)
      Low[data&0XFFFF]++;//记录高16位为HighNumber的数的低16位–yqshare
   x++;
}
x = i ;
while(x>=2T)
{
   x += Low[i++]; //累计大小–yqshare
}
LowNumber = (–i)&0xFFFF;//添加上低16位–yqshare
Number = HighNumber | LowNumber ;

这样就可以求出该数了。

分类: 算法讨论, 编程 标签:
  1. 2010年8月29日18:18 | #1

    又一个伟大的程序员

  2. 小卢
    2010年8月29日22:48 | #2

    @小康

    呵呵,探讨,都是探讨.

  3. 算法之神
    2010年9月3日09:14 | #3

    第二个算法,真是经典…

  4. 2010年9月3日13:00 | #4

    3D武侠巅峰之作《武林风云》震撼内测
    官方网站www.wlfygame.com
    天下第一的百变招式动作
    每个招式动作细腻讲究,透过动态捕捉技术完整呈现『真.武侠』的游戏魅力,让您拥有各门派的精致武学,成就您当武林大侠的愿望

  5. 2010年9月6日16:49 | #5

    不错,支持个

  6. 2010年9月7日22:42 | #6

    最近特喜欢看你这方面的博客..很好看。

  7. 2010年9月14日09:25 | #7

    博主这个方法很好啊,也不难理解

  8. 2010年9月20日00:09 | #8

    很久都没有来了,上次来是什么时候都快忘记了

  9. Harvey
    2010年9月20日16:55 | #9

    很好的解答。指出两个有误的地方,如果讲的不对,请指出。

    1. 解法1需要读和写,所以IO应该最坏是O(64*n).

    2. 解法2,低16位的求法错误。第2T个数的低16位可以是0xFFFFFFFF到0×0之间的任何一个。只要他的排位刚好是第2T个就行了。

    低16位的解法应该是:
    1>确定第2T个数在所有高16位相同的数中的相对位置k。比如此高16位的数目共有1000个,循环退出时,x=2T+500,则第2T个数在这1000个里是第500大的数,问题转化为求1000个乱序的数组中第500大的数。
    2>再扫描一次文件,对所有高16位的数字建立小顶堆,大小为k,第k+1到最后一个数跟堆顶比,如果比堆顶小,则继续扫描;否则替换堆顶,调整堆为小顶堆。最后即可得到第k的数,也及在整个文件为2T大的数。

  10. 2010年9月25日22:40 | #10

    我来转了好几圈了

  11. 2010年10月3日16:25 | #11

    好深奥的说

  12. sunixfu
    2010年10月6日19:04 | #12
  13. balbo
    2011年4月10日19:48 | #13

    x = 0 ;
    i = 0;
    while(x>=2T) 这是怎么回事?

  14. balbo
    2011年4月10日20:22 | #14

    如果data是个负数 High[data>>16]++;这样会有问题吗?

  15. 2011年9月23日09:44 | #15

    这个真的看不懂,汗了。。。

  1. 本文目前尚无任何 trackbacks 和 pingbacks.

Site Optimization by PHP Speedy Site Optimization by PHP Speedy