大师级的文章,总是能够使你更接近于事物的本质。
 
最近看了pongba的数学之美番外篇:快排为什么那么快。文中提到了Mackay的一篇文章(这里是译文),里面提到了使用信息论来解释快排与堆排的速度差异的本质原因。看罢,内心有种莫名的激动。虽不懂信息论,但Mackay(大师毕竟是大师),最简单的解释,却直接触到了本质。有兴趣的可以看看Mackay的这篇文章。这里,只是我自己的一点感悟。
信息熵是什么?
一个事件,它的信息量大小和它的不确定性有直接的关系。比如说,我们要搞清楚一件非常非常不确定的事,或是我们一无所知的事情,就需要了解大量的信息。相反,如果我们对某件事已经有了较多的了解,我们不需要太多的信息就能把它搞清楚。所以,从这个角度,我们可以认为,信息量的度量就等于不确定性的多少。 
我们把这个信息的度量叫做“熵”,熵越大表明这个事件的结果越难以预测,同时事件的发生将给我们带来越多的信息。
大家都掷过硬币。我们把掷(正常的)硬币这个事件的熵看做是1bit,为什么把一次比较的结果看成是1bit的信息呢?
可以这么解释:
一个抛硬币 正反各 1/2 概率。 如果信号源是这个, 所含有的信息量就是 - 1/2 * log (1/2) + -1/2 * log(1/2) = 1 bit 。
所以, 一个能生成 1/2、 1/2 概率(等概率)分布的信号所含的信息量就是 1bit 。比较就是这样的运算, 看作 cmp(a, b) 看作是抛硬币. 每次返回的是1个 bit。 有人会质疑,因为有时候要满足序的性质,  如 a>b, b>c 则 a>c 导致第三次问 cmp(a, c) 的时候不能以 1/2 1/2 的 概率生成随机信号. 其实, 一个好的排序算法就是避免使用了 cmp(a, b) cmp(b, c) 得到 a>b b>c 后还比较一次 a, c. 最佳的排序算法, 就是每次都能从 cmp 得到1bit 的信息。
但是,如果这个硬币被做过手脚,使得正面的概率大于反面的概率(或者反相反),那么这个事件的熵将<1bit。因为,我们对于结果的情况已经有所知道,显然,得到的信息也就少了。
更极端的情况,如果这个硬币是两个正面(或者相反),那么这个事件的熵将为0 bit,因为,我们完全知道结果是怎么样的。
1、排序与信息熵
为什么可以将信息论延伸到(基于比较的)排序?
熵是信息科学中的一个重要概念, 用来表示事物的不确定性. 排序就是使序列从无序到有序的过程, 无序就是不确定性, 因此可以用信息熵的概念来描述序列的无序程度,进而研究基于比较的排序算法的效率。

所以,想让基于比较的排序更快,等价于让每次比较的信息熵达到最大。所以,让每次比较的结果概率相等(使得每次比较的信息熵接近1bit),这是算法改进的核心思想。

给出一个题目:有5个数,最少需要几次比较,来实现数据的排序?
设序列S 长度为n, 则该系列共有x = n!种不同的排列, 若将该序列看做信源的输出, 设每种排列出现的概率相同, 则根据离散随机变量的信息熵的定义, 序列S 的平均信息量, 即信息熵定义为:
Hn = l(x ) = log2(x ) =log2n! (bit)
5个整数,产生的全排列有 5!=120 种,从信息论的角度,信息量就是log2{120} = 6.90689059560852 bit,而两个数比较一次,最多可以产生1bit的信息量,所以7次比较几乎就是极限了。
    
当然这不是说你比较7次就可以得出排序结果的,而是当你尝试了无数次之后,发现,7次是它的极限次数。
 
下面,我们来看看,运用每次比较得到的信息熵接近于1bit的思想,来具体的实现,上诉的排序过程:
    设原序列由(a, b, c, d , e ) 5 个元素组成,
    首先分别对(a, b) 和(c, d ) 进行排序, 设排序结果分别为[a b ] 和[ c d ] ( [ x y ] 表示已排序) (对于其他排序结果, 以下分析过程相同) , 显然这一步需要2 次比较.
    然后比较a 与c, 若a < c, 则排序结果为[a c d ], 用折半插入法经过2 次比较将e 插入到[ac d ] 当中,
    最后将b 插入到已排序的序列, 由于已知b > a, 所以用折半插入法插入b, 最坏情况下只需要2 次比较. 若a > c, 排序结果为[ c a b ], 同样用折半插入法经2 次比较将e 插入, 由于c 已与d比较过, 因此最后将d 插入到已排序的序列最多也只需要2 次比较.
    综上, 整个排序过程最多只需要7次比较. 第一步a 与b 及c 与d 的比较显然各减少1 bit 信息熵, 此时序列还有30 种不同的排列, 剩余的信息熵为4. 9069 b it. a 与c 比较后, 序列还剩15 种排列, 剩余的信息熵为3. 9069 b it, 所以这次比较也减少信息熵1 bit. 插入e 后, 若e < a, 序列可能出现3 种排列, 若e > a, 则有16 种可能的排列, 因此其平均信息熵为:

所以2 次比较减少信息熵1.9724 bit, 最后2 次比较减少1.9345 bit. 可见每次比较减少信息熵接近1 bit, 因此排序效率达到最高。
 
2、冒泡、选择、插入,为什么那么慢?
 
首先我们来看三种经典的平方复杂度算法。它们的效率并不高,原因就在于算法过程中会出现越来越多概率严重不均的比较。
1、冒泡
随着冒泡排序的进行,整个序列将变得越来越有序,位置颠倒的泡泡将越来越少;
2、选择排序
选择排序的每一趟选择中,你都会不断得到越来越大的数,同时在以后的比较中找到更大的数的概率也越来越低;
3、插入排序
在插入排序中,你总是把新的数与已经排好的数按从大到小的顺序依次进行比较,可以想到新的数一开始就比前面所有的数中最大的那个还大的概率是相当小的。受此启发,我们可以很自然地想到一个插入排序的改进:处理一个新的数时,为何不一开始就与前面处理过的数中的中位数进行比较?这种比较的熵显然更大,能获取的信息量要大得多,明显更有价值一些。这就是插入排序的二分查找改进。
3、快排为什么那么快?
 
    快速排序算法中,比较的信息熵不会因为排序算法的进行而渐渐减小,这就是快速排序比上面几个排序算法更优秀的根本原因。
    仔细回顾快速排序算法的过程,我们可以看出,每次比较的两种结果出现的概率完全由这一趟划分过程所选择的基准关键字决定:如果选择的基准关键字刚好是当前处理的数字集合的中位数,则比较结果的不确定性达到最大一次比较的信息熵达到1bit)。这个时候,快排的比较次数,也就等于排序所需要的总信息熵:log2n! (bit) 这也正是快排的时间复杂度。
 
4、快排又为不那么快?
如果选择的基准关键字过大或过小,都会出现比较产生的结果不均等的情况,这使得每次比较平均带来的信息量大大减少。
比如:选择的主元为最小值,那么跟这个主元进行的比较得到的信息熵为0bit。因为,每次的结果都是肯定的。
 
看看主元随机时候的情况:

当主元随机选择的时候:我们不妨令轴元素为pivot,第一次比较结果是a1<pivot,那么可以证明第二次比较a2也小于pivot的可能性是2/3!这容易证明:如果a2>pivot的话,那么a1,a2,pivot这三个元素之间的关系就完全确定了——a1<pivot<a2,剩下来的元素排列的可能性我们不妨记为P(不需要具体算出来)。而如果a2<pivot呢?那么a1和a2的关系就仍然是不确定的,也就是说,这个分支里面含有两种情况:a1<a2<pivot,以及a2<a1<pivot。对于其中任一种情况,剩下的元素排列的可能性都是P,于是这个分支里面剩下的排列可能性就是2P。所以当a2<pivot的时候,还剩下2/3的可能性需要排查。

再进一步,如果第二步比较果真发现a2<pivot的话,第三步比较就更不妙了,模仿上面的推理,a3<pivot的概率将会是3/4!

这就是快排也不那么快的原因,它不能保证每次比较结果的概率都是相同的(1/2 :1/2),因此,每次比较的信息熵不是都=1bit。

如果你想改进快排,那么可以试试把精力放在提高每次比较的信息熵

from: http://blog.csdn.net/cyh_24/article/details/8120045

快排Quick Sort到底有多快?相关推荐

  1. SEO快排是什么?怎样实现快排?

    转自:http://www.weidianyuedu.com SEO快排是什么?顾名思义SEO快排就是快速排名,也就是让指定关键词指定内容快速在搜索引擎获得排名.SEO优化是一个长期的过程,而SEO快 ...

  2. js排序(快排与sort)

    快排 <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8&qu ...

  3. 非递归的方法写快排java_快排的最差情况以及快排平均复杂度的计算

    最近突然讨论了这两个问题,有点忘记了,记录了一下网上的比较好的说法,参见Reference 快排的相关知识请参考排序总结 快排的最差情况以及如何避免 首先,快排的最差情况什么时候发生? 1. 已排序 ...

  4. seo模拟点击软件_关键词快排是什么?SEO快排、刷点击和快排发包原理分析

    如今大多数SEO快排都宣称自己是安全的SEO发包技术,但这种可能的发包技术原理更能解释为什么百度指数比百度表现更好. 如今,带来的是"用户行为影响排名.刷点击和快排分配原则".但愿 ...

  5. 23行代码_动图展示——快排详解(排序最快的经典算法)

    快排 1.快排的实现逻辑: 先从数列中取出一个数作为基准数(通常取第一个数). 遍历序列,将比它小的数与比它大的数分别记录下来,分为两类,最后该数放在这两类数中间(它左边的所有数都比它小,右边的所有数 ...

  6. 天下武功,唯快不破 - SAP HANA 到底有多快?

    天下武功,无坚不破,唯快不破,以势赢者势颓则败,以力胜者力尽则亡. 但是武功在高,也怕"羊刀"啊一神器,(又名:邪恶镰刀,名字是不是很霸气,其实就是其能将敌方单位变为小动物的能力- ...

  7. 快排Java代码实现(Quick Sort)

    1.  快排算法思路 基本思想:通过一趟快速排序将待排数组分割成独立的两份部分; 其中一部分数组的值均比另一部分数组的值小,则可分别对着两部分数组继续进行排序,以达到整个序列有序. 快排的平均时间复杂 ...

  8. 排序算法--快排的优化

    排序算法–快排的优化 下面是我写的一种快排: #include <iostream> #include <stdlib.h>using namespace std;void P ...

  9. C++数据结构和算法2 栈 双端/队列 冒泡选择插入归并快排 二三分查找 二叉树 二叉搜索树 贪婪 分治 动态规划

    C++数据结构和算法2 栈 双端/队列 冒泡选择插入归并快排 二三分查找 二叉树 二叉搜索树 贪婪 分治 动态规划 博文末尾支持二维码赞赏哦 _ github 章3 Stack栈 和 队列Queue= ...

最新文章

  1. 干货:如何在前端统计用户访问来源?
  2. [团队开发]总结下Server 2008 + TeamFoundation Server 2008安装过程
  3. 单循环链表中设置尾指针比设置头指针更好的原因
  4. DevExpress控件库----AlertControl提示控件
  5. 博士生的deadline血泪史,这是一份来自Nature的避坑指南
  6. Java 网络实例一(获取指定主机的IP地址、查看端口是否已使用、获取本机ip地址及主机名、获取远程文件大小)
  7. Java线程之CompletionService批处理任务
  8. idea 批量导入包
  9. 《可解释的机器学习》校对活动正式启动 | ApacheCN
  10. 2022年美赛成绩什么时候出,2022美赛思路与注意事项。
  11. 超简单版Python打包exe文件,并修改图标,这将是你见过最容易上手的教程~
  12. CentOS7中安装PostgreSQL客户端
  13. 无线系统笔记(1)--梯度、散度、旋度(麦克斯韦先导)
  14. js 实现当有省略号时,显示title,无省略号不显示title
  15. mysql 取月份天数_mysql 之 获取指定月份天数和指定月份上月天数
  16. java jar包转成exe运行
  17. 计算机网络与互联网的区别,计算机网络与互联网的主要区别是什么?
  18. 肿瘤NGS的常规检测流程
  19. 成都开发者看过来!百度资深研发工程师将出席超级账本成都见面会
  20. Qt 模型视图编程之表头设置

热门文章

  1. 主成分分析(PCA)——以2维图像为例
  2. android 蓝牙耳机 判断,Android实现蓝牙耳机连接
  3. Tomcat - Tomcat 8.5.55 启动过程源码分析阶段三_start阶段
  4. MySQL-日志二进制日志binlog初探
  5. python中key的意思_有朋友问Python 中实例对象为啥能按照key赋值。
  6. expdp oracle 并行_关于Expdp/Impdp 并行导入导出详细测试结果和并行参数的正确理解!!...
  7. 项目实战解决 java.sql.SQLException: Unable to load authentication plugin ‘caching_sha2_password‘.
  8. Go语言垃圾回收(GC)
  9. php函数dirname范例,PHP dirname( )用法及代碼示例
  10. php网站添加cnzz,cnzz代码添加元素到页面