以前帮学长做毕设写算术编码的时候,粗略地接触过熵的概念。至于信息论这么重量级的名词基本碰到都果断绕开。今天看到刘未鹏的博客中的文章“ 数学之美番外篇:快排为什么那样快 ”。我学到了不少,但是有了很多疑问:
首先:排序每次的比较尽量排除一半的可能性,就好比用svm在复杂解空间中找到有效地分类(均分)方法。而排序算法的下限O(n*log(n))=O(log2(P(n,n)))的近似,这里解空间的大小为P(n,n),P为排列数,求唯一的最优的排序,最快即是二分,复杂度O(log(m)),m代成P(n,n),即得到。
其次,快排之所以慢是因为在一轮的比较中,前面比较得到的信息没有被利用,设想对于一个pivot,当比较完a1次后,解空间其实被三等分,若a1<pivot,则k<a1,a1<k<pivot,k>pivot。你要是不明白我说的,就要好好理解解空间,设想当a1,pivot确定位置关系后,剩下的数可能的位置所构成的所有排列,为一个巨大的当前探知的解空间,而这个解空间被a1,pivot三等分可以通过乘法原理轻松证得:ak可以有三种选择。。那么这时其实再用a2和pivot比较就好像无视新划分的解空间,也就好像还是用pivot的二分解空间继续划分。这就是不够充分利用先前比较得到的信息。(刘未鹏引用书中的理解是每次分割淘汰的可能性小,意思也差不多,不过不够直观,不易理解)。然后,是堆排序,之所以慢,原文解释是因为每次比较的分割解空间不够均匀,只是分掉一小部分,比较提供的信息不足。我觉得这个解释没有说服力,完全没有考虑堆的结构特性!堆是二分的,已经二分了,如果均匀的二分效率一定很高。如果动态的调整保证其均匀,效率会更高。但是在最坏情况下,当堆退化为一条线时,原文的分析才正确,这时复杂度根本不是O(n*log(n))是,O(n^2)。所以分析堆的效率不能那么简单的分析。要考虑其结构。而MacKay提到的优化,也不解决问题,同样没有利用堆二分的结构。其实要想让堆的比较速度快,只需使堆尽量平衡。而取大数rebuilt堆和取小数rebuilt堆没有任何关系:因为每次从上往下移动都是一次二分查找。效率为O(log(n)),无论你用较大的数比较还是较小的数比较。只要当树不平衡时才会退化。看到这里你会认为用信息论的知识似乎根本解决不了堆排序的问题。。其实不然,我们忽略了建堆所做的事情,建堆使得解空间不再均匀分布,所以建堆之后的比较并没有去除确定比例的解空间。也许不是直观可看到的,但应该通过细致的运算可以算出。那就不是我能做的了。。。