新的快速排序算法: 《Dual-Pivot QuickSort》阅读笔记

相信大家在大学的《算法与数据结构》里面都学过快速排序(QuickSort), 知道这种排序的性能很好,JDK里面直到JDK6用的都是这种经典快排的算法。但是到了JDK7的时候JDK内置的排序算法已经由经典快排变成了Dual-Pivot排序算法。那么Dual-Pivot到底是何方圣神,能比我们学过的经典快排还要快呢?我们一起来看看。

经典快排

在学习新的快排之前,我们首先来复习一下经典快排,它的核心思想是:

接受一个数组,挑一个数(pivot),然后把比它小的那一摊数放在它的左边,把比它大的那一摊数放在它的右边,然后再对这个数左右两摊数递归的执行快排过程,直到子数组只剩一个数为止。[2]

伪代码大概是这样的:

void quicksort(int array[], int left, int right)
{
    //Do nothing if left <= right
    //p <- Get a number from array
    //Put elements <= p to the left side
    //Put elements >= p to the right side
    //Put p in the middle slot which index is pivot
    //Recursive quicksort the left parts and right parts
}

元素比较的次数

经典快排为什么快? 所谓快其实专业的说法是“时间复杂度”。对于排序算法来说主要看的是排序所需要的元素比较的次数

我们在《算法与数据结构》里面都是这么学的。

对于快排来说,假设输入数组里面数字的个数为n,那么平均要进行的元素之间比较的次数是: O(nlogn)。相比冒泡排序的O(n2)来说比较的次数要少,那么当然就快了。

Dual-Pivot快排是个什么鬼?

它是俄罗斯人Vladimir Yaroslavskiy在2009年开发出来的。要知道经典快排在1990年代稳定下来就再也没有大改过了,几乎所有的语言里面的排序用的都是同样的经典快排的算法。20年之后当这个俄罗斯人提出新的Dual-Pivot快排的时候,很多人的第一想法是不信的,因为经典快排都被研究烂了,大家不相信在这个算法上面还会有什么可以改进的地方。

那么Dual-Pivot快排到底是怎么样的一个排序算法呢?其实从它的名字里面可以看出一些端倪:在经典快排里面有一个pivot的概念,它是用来分隔大数和小数的 -- 这个pivot把一个数组分成两份。那么所谓的Dual-Pivot其实就是用两个Pivot, 把整个数组分成三份。伪代码大概是这样的:

dual_pivot_quicksort(A,left,right) // sort A[left..right]
    if right−left ≥ 1
        // Take outermost elements as pivots (replace by sampling)
        p := min {A[left],A[right]}
        q := max{A[left],A[right]}
        ℓ := left +1; g := right −1; k := ℓ
        while k ≤ g
            if A[k] < p
                 Swap A[k] and A[ℓ]; ℓ := ℓ+1
            else if A[k] ≥ q
                while A[g] > q and k < g
                    g := g −1
                end while
                Swap A[k] and A[g]; g := g −1
                if A[k] < p
                    Swap A[k] and A[ℓ]; ℓ := ℓ+1
                end if
            end if
            k := k +1
        end while
        ℓ := ℓ−1; g := g +1
        A[left] := A[ℓ]; A[ℓ] := p // p to final position
        A[right] := A[g]; A[g] := q // q to final position
        dual_pivot_quicksort(A, left , ℓ−1)
        dual_pivot_quicksort(A, ℓ+1,g −1)
        dual_pivot_quicksort(A,g +1,right)
    end if

看起来主要的区别就是经典快排递归的时候把输入数组分两段,而Dual-Pivot则分三段,就这么简单,那为什么这就快了呢?
其实如果按照元素比较次数来比较的话,Dual-Pivot快排元素比较次数其实比经典快排要多:

1.5697nlnn VS 1.7043nlnn

理论跟实际情况不符合的时候,如果实际情况没有错,那么就是理论错了。

CPU与内存

要理解上面的问题,先介绍点背景知识。我们平常很少考虑过CPU的速度内存的速度CPU和内存速度是否匹配的问题。

其实它们是不匹配的。

距统计在过去的25年里面,CPU的速度平均每年增长46%, 而内存的带宽每年只增长37%,那么经过25年的这种不均衡发展,它们之间的差距已经蛮大了。

假如这种不均衡持续持续发展,有一天CPU速度再增长也不会让程序变得更快,因为CPU始终在等待内存传输数据,这就是传说中内存墙(Memory Wall)。

有点像木桶理论:木桶的容量是由最短的那块板决定的,所以你CPU再快,如果内存带宽不够,那也没用。

25年前Dual-Pivot快排可能真的比经典快排要慢,但是25年之后虽然算法还是以前的那个算法,但是计算机已经不是以前的计算机了。在现在的计算机里面Dual-Pivot算法更快!

扫描元素个数

那么既然光比较元素比较次数这种计算排序算法复杂度的方法已经无法客观的反映算法优劣了,那么应该如何来评价一个算法呢?作者提出了一个叫做扫描元素个数的算法。

在这种新的算法里面,我们把对于数组里面一个元素的访问: array[i] 称为一次扫描。但是对于同一个下标,并且对应的值也不变得话,即使访问多次我们也只算一次。而且我们不管这个访问到底是读还是写。

其实这个所谓的扫描元素个数反应的是CPU与内存之间的数据流量的大小。

因为内存比较慢,统计CPU与内存之间的数据流量的大小也就把这个比较慢的内存的因素考虑进去了,因此也就比元素比较次数更能体现算法在当下计算机里面的性能指标。

在这种新的算法下面经典快排和Dual-Pivot快排的扫描元素个数分别为:

1.5697nlnn VS 1.4035nlnn

也就是说经典快排确实进行了更多的元素扫描动作,因此也就比较慢。在这种新的算法下面,Dual-Pivot快排比经典快排t节省了12%的元素扫描,从实验来看节省了10%的时间。

结论

  • 由于CPU与内存的发展失衡,我们在分析算法复杂性的时候已经不能简单地用元素比较次数来比较了,因为这种比较的方法只考虑了CPU的因素,没有考虑内存的因素。
  • 对于那种对输入数据进行顺序扫描的排序算法,扫描元素的个数这种新的算法把内存的流量的因素考虑进去,比较适应新时代。
  • 世界在不断的变化,你当年在学堂里面学的东西可能已经过时了。

参考资料

  1. Why Is Dual-Pivot Quicksort Fast?
  2. 一个Quicksort究竟可以写到多么短
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容