今天帮助David 调程序,他的程序是做Windows下目标程序分析,其中有个数组存放目标程序heap中所有数据结构,David的程序遍历目标程序全局数据段所有内容,如果某个值落在heap中某个数据结构地址范围内,就把它找出来,并建立从全局数据段到该数据结构的一个路径。
David给我演示时, 说运行Toy program 没问题,但运行一个比较大的真实程序时,就会出现性能问题,果然,我们等了二十多分钟,还是没有算完。于是我检查了他的代码,告诉他问题可能出在检查指针是否指向有效数据结构时,他是用的线性查找方法,如果试着使用二分查找法,可能会快很多,因为算法复杂度从O(N)降到了O(logN)。没多久,他就写好了新的程序。同样的大程序,不到一分钟就算好了,他提到昨天他做过同样实验,大概需要1个小时才能算完,再次检查他的程序,大程序中heap中的数据结构有几千个, 难怪二分查找比线性查找性能好这么多。
我于是想到经典书籍《编程珠玑(Programming Pearls)》, 该书花了大量篇幅来讲二分查找,2, 4, 9, 11, 13章都有直接讲到二分查找,可见二分查找的重要性。第二章有段话精确地描述了我今天碰到的例子,如下:“程序员一开始可使用简单的顺序查找数据结构。顺序查找通常也够快了。如果程序变得太慢了,那么对表进行排序然后使用二分查找往往可以排除这个瓶颈。”
第二章提出了三个典型问题,其中问题A如下:“给定一个包含32位整数的顺序文件,它至多只能包含40亿个这样的整数,并且整数的次序是随机的。请查找一个此文件中不存在的32位整数。在有足够主存的情况下,你会如何解决这个问题?如果你可以使用若干外部临时文件但可以用主存却只有上百字节, 你会如何解决这个问题?”
如果我们有足够内存,当然很简单,不需要使用二分查找法。但如果只有上百字节内存, 线性查找就无法使用了。我们使用二分查找,设定2^32 / 2 为中点M(假设都为正整数,无负数),然后遍历文件中所有整数,把大于M的放一个临时文件,小于M的放另一个临时文件,并计算每个文件中整数个数,如果小于2^32 / 2 个,那么该文件的整数中至少有一个漏洞,重复以上过程就能找到一个文件中不存在的32位整数了。二分查找法让这个问题突然变得简单了好多。就如书中所说“看起来很困难的问题解决起来可能很简单,并且还很可能出人意料... ... 只有经过广泛的研究之后才能对算法具备那种出神入化的理解力。 ”
感谢David的程序和《编程珠玑》, 让我对二分查找有了更为深入的理解。