Day-18 BFPRT演算法

最坏情况为https://chart.googleapis.com/chart?cht=tx&chl=%5CTheta(n),BFPRT演算法

在由随机数决定阵列的分割的情况下,我们如何避免产生出最差情况(虽然出现的机率很小),或是让最差的情况时间复杂度也是https://chart.googleapis.com/chart?cht=tx&chl=%5CTheta%20(n)

BFPRT演算法(由 Blum, Floyd, Pratt, Rivest 与 Tarjan 创造)可以实现出这一件事情,这是一个具有确定性(deterministic)的演算法,也就是不含任何随机的成分。

我们会产生出最差的情况为分割极度不平衡,也就是产生出https://chart.googleapis.com/chart?cht=tx&chl=0%3An-1这种分割,而会产生出这种分割,和我们的主元(pivot)的选择很有关系,如果我们可以保证选择到的主元(pivot)都是好的,确保不会产生出最差分割,这个主元是确定是最好的,也就是在样本空间无限大时,每一次的主元都是好的,而不是机率上趋近於最好。那麽这个演算法就可以避免掉最差情况的发生。而这就是BFPRT演算法的主要想法。

BFPRT演算法遵循以下五个步骤

  1. 将n个元素的https://chart.googleapis.com/chart?cht=tx&chl=A阵列(不一定要是阵列)拆分成https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D组,每一组含有5个元素,最後一组由https://chart.googleapis.com/chart?cht=tx&chl=n%5C%20%7Bmod%7D%5C%205组成,如果https://chart.googleapis.com/chart?cht=tx&chl=n%3D1,则直接回传https://chart.googleapis.com/chart?cht=tx&chl=A%5Bn%5D
  2. 寻找这https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clceil%7Dn%2F5%7B%5Crceil%7D组中每一组的中位数,方法为对每一组元素使用insertion sort,排序後找出中位数,每一组都有五个元素,因此经过排序後地三个元素即为中位数。
  3. 在2.中会找出https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clceil%7Dn%2F5%7B%5Crceil%7D个中位数,把这些中位数也组成一个阵列,在这个阵列中找到中位数https://chart.googleapis.com/chart?cht=tx&chl=x(透过递回呼叫BFPRT)。
  4. 在3.产生出由中位数产生的阵列,以https://chart.googleapis.com/chart?cht=tx&chl=x作为主元(pivot)对阵列进行划分。比https://chart.googleapis.com/chart?cht=tx&chl=x小的元素划分到https://chart.googleapis.com/chart?cht=tx&chl=x的左边,https://chart.googleapis.com/chart?cht=tx&chl=x本身被划分到左边,而比https://chart.googleapis.com/chart?cht=tx&chl=x大的元素划分到左边。
  5. https://chart.googleapis.com/chart?cht=tx&chl=k定义为https://chart.googleapis.com/chart?cht=tx&chl=k%20%3D%20rank(x),也就是将https://chart.googleapis.com/chart?cht=tx&chl=k当作https://chart.googleapis.com/chart?cht=tx&chl=x在阵列中大小的排名,而输入的参数https://chart.googleapis.com/chart?cht=tx&chl=i表示我们要寻找阵列中第https://chart.googleapis.com/chart?cht=tx&chl=i大的元素。
    如果https://chart.googleapis.com/chart?cht=tx&chl=i%3Dk,则直接回传https://chart.googleapis.com/chart?cht=tx&chl=x
    如果https://chart.googleapis.com/chart?cht=tx&chl=i%20%3C%20k,则在阵列中左分割的部分,也就是比https://chart.googleapis.com/chart?cht=tx&chl=x小的部分递回呼叫BFPRT
    如果https://chart.googleapis.com/chart?cht=tx&chl=i%20%3E%20k,则在阵列中右分割的部分,也就是比https://chart.googleapis.com/chart?cht=tx&chl=x大的部分递回呼叫BFPRT

范例 : 输入一个有34个元素的A阵列,https://chart.googleapis.com/chart?cht=tx&chl=A%5B34%5D

  1. 将n个元素的https://chart.googleapis.com/chart?cht=tx&chl=A阵列拆分成https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D组,每一组含有https://chart.googleapis.com/chart?cht=tx&chl=5个元素,最後一组由https://chart.googleapis.com/chart?cht=tx&chl=n%5C%20%7Bmod%7D%5C%205个元素组成。
  2. 寻找这https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clceil%7Dn%2F5%7B%5Crceil%7D%3D7组中每一组的中位数
  3. 在2.中会找出https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clceil%7Dn%2F5%7B%5Crceil%7D%3D8个中位数,把这些中位数也组成一个阵列,在这个阵列中找到中位数https://chart.googleapis.com/chart?cht=tx&chl=x(透过递回呼叫BFPRT)。
  4. 在3.产生出由中位数产生的阵列,以https://chart.googleapis.com/chart?cht=tx&chl=x作为主元(pivot)对阵列进行划分。比https://chart.googleapis.com/chart?cht=tx&chl=x小的元素划分到https://chart.googleapis.com/chart?cht=tx&chl=x的左边,https://chart.googleapis.com/chart?cht=tx&chl=x本身被划分到左边,而比https://chart.googleapis.com/chart?cht=tx&chl=x大的元素划分到左边。

    https://chart.googleapis.com/chart?cht=tx&chl=x的排名为第3名,令https://chart.googleapis.com/chart?cht=tx&chl=k%3Drank(x),中位数组成的子阵列有7个元素,https://chart.googleapis.com/chart?cht=tx&chl=n%3D7,右边的分割有https://chart.googleapis.com/chart?cht=tx&chl=n-k%3D4个元素,左边分割有https://chart.googleapis.com/chart?cht=tx&chl=n-k-1%3D3个元素
  5. 接着通过递回呼叫找到我们要的元素

我们把上面这个阵列看作是一张图(graph),我们在两个节点(或是元素)定义以下符号,表示https://chart.googleapis.com/chart?cht=tx&chl=a%20%3C%20b

我们把这样的符号,加到上面的图上

我们也对中位数构成的子阵列加上这样的符号

得到这张非常混乱,就像是我的面包版的图

我们使用箭头,表示是一个有向路径,也就是我们走访的路线,我们可以透过任一条路径,得知两个元素之间的大小关系,且通过这个路径,我们可以知道阵列每一个区块和https://chart.googleapis.com/chart?cht=tx&chl=x之间的关系。

由箭头我们可以知道https://chart.googleapis.com/chart?cht=tx&chl=a%20%3C%20bhttps://chart.googleapis.com/chart?cht=tx&chl=b%20%3C%20x,因此https://chart.googleapis.com/chart?cht=tx&chl=a%20%3C%20xhttps://chart.googleapis.com/chart?cht=tx&chl=a上面的元素也必定小於https://chart.googleapis.com/chart?cht=tx&chl=x,旁边的分组也是如此,因此我们可以知道,灰色区域框住的阵列区块中所有元素皆小於等於https://chart.googleapis.com/chart?cht=tx&chl=x

而橘色部分中所有元素,必定大於等於https://chart.googleapis.com/chart?cht=tx&chl=x

由上面这张图我们可以知道,每一个分组中有https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%203%205的元素会小於等於https://chart.googleapis.com/chart?cht=tx&chl=x,而我们将这个含有https://chart.googleapis.com/chart?cht=tx&chl=34个元素的阵列拆分成https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2B1组(https://chart.googleapis.com/chart?cht=tx&chl=%2B1为含https://chart.googleapis.com/chart?cht=tx&chl=n%20%5C%20mod%5C%205个元素的组),其中有https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%7B%5Crfloor%7D的组会存在https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%203%205的元素会小於等於https://chart.googleapis.com/chart?cht=tx&chl=x这个性质,因此,我们可以推导出以下性质:

给定https://chart.googleapis.com/chart?cht=tx&chl=n个元素的https://chart.googleapis.com/chart?cht=tx&chl=A阵列 :

  • https://chart.googleapis.com/chart?cht=tx&chl=3%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%7B%5Crfloor%7D个元素小於等於https://chart.googleapis.com/chart?cht=tx&chl=x,(在由中位数划分出的子阵列中,有https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2个元素小於等於https://chart.googleapis.com/chart?cht=tx&chl=x)

而每一个分组中,也至少会有https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%203%205个元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x,而我们将含有https://chart.googleapis.com/chart?cht=tx&chl=34个元素的阵列猜分成https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%3D6%2B1组,其中有https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%7B%5Crfloor%7D%2B2会具有https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%203%205的元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x这个性质,

给定https://chart.googleapis.com/chart?cht=tx&chl=n个元素的https://chart.googleapis.com/chart?cht=tx&chl=A阵列 :

  • https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%203%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%2B2%7B%5Crfloor%7D个元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x,(在由中位数划分出的子阵列中,有https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%2B2个元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x)

BFPRT效率分析

由上面的推论,我们可以得到以下性质

  • https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%203%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%7B%5Crfloor%7D%20%3E%3D%20%5Cfrac%20%7B3n%7D%20%7B10%7D个元素小於等於https://chart.googleapis.com/chart?cht=tx&chl=x
  • https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%203%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%20%2B%202%7B%5Crfloor%7D%20%3E%3D%20%5Cfrac%20%7B3n%7D%20%7B10%7D%20%2B%206个元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x

由上面这个性质我们可以知道,最多我们可以产生出一个https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%200%3A%5Cfrac%20%7B3n%7D%20%7B10%7D%20%2B%20%5Cfrac%20%7B3n%7D%20%7B10%7D%20%2B%206个元素的分割情况,为了求上界,我们简化成最多产生出https://chart.googleapis.com/chart?cht=tx&chl=0%3A%5Cfrac%20%7B7n%7D%20%7B10%7D的分割情况,也就是在第5步中,BFPRT递回呼叫做多作用在https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%20%7B7n%7D%20%7B10%7D个元素上。

下面推导BFPRT演算法最坏情况的执行时间https://chart.googleapis.com/chart?cht=tx&chl=T(n)

  1. 拆分阵列需要https://chart.googleapis.com/chart?cht=tx&chl=O(n)
  2. 固定阵列大小的排序,需要的时间是固定的,因此为https://chart.googleapis.com/chart?cht=tx&chl=O(n)
  3. 递回呼叫需要https://chart.googleapis.com/chart?cht=tx&chl=T(%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D)
  4. 划分需要https://chart.googleapis.com/chart?cht=tx&chl=O(n)
  5. 递回呼叫最多需要https://chart.googleapis.com/chart?cht=tx&chl=T(%5Cfrac%20%7B7n%7D%20%7B10%7D)

因此,https://chart.googleapis.com/chart?cht=tx&chl=T(n)的上限为以下关系式
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20T(n)%3C%3DT(%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D)%2BT(%5Cfrac%20%7B7n%7D%20%7B10%7D)%2BO(n),使用代换法求解

假设https://chart.googleapis.com/chart?cht=tx&chl=T(n)%3C%3Dcn,则
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20T(n)%3C%3DT(%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D)%2BT(%5Cfrac%20%7B7n%7D%20%7B10%7D)%2BO(n)
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20%5C%5C%3D1%2F5*cn%2B7%2F10*cn%2BO(n)
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20%5C%5C%3D9%2F10*cn%2BO(n)
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20%5C%5C%3Dcn-(1%2F10*cn-O(n)),如果常数https://chart.googleapis.com/chart?cht=tx&chl=c足够大,则https://chart.googleapis.com/chart?cht=tx&chl=(1%2F10*cn-O(n))为非负的项,观察可以发现,只要我们https://chart.googleapis.com/chart?cht=tx&chl=chttps://chart.googleapis.com/chart?cht=tx&chl=10的任意倍数,就可以实现了,而当https://chart.googleapis.com/chart?cht=tx&chl=n%20%3C%3D%2050,则https://chart.googleapis.com/chart?cht=tx&chl=T(n)https://chart.googleapis.com/chart?cht=tx&chl=O(1),如果https://chart.googleapis.com/chart?cht=tx&chl=c足够大,则https://chart.googleapis.com/chart?cht=tx&chl=T(n)的上限就是https://chart.googleapis.com/chart?cht=tx&chl=cn,也就是线性时间。


<<:  Hook 概观( Day15 )

>>:  D23 Django-debug-tools 失败

Ruby 实体变数(instance variable)

在 Ruby 里的实体变数是有一个 @ 开头的变数,顾名思义,是活在每个实体里的变数,而且每个实体之...

[Day4] Web 小花花

[Day4] Web 小花花 不要问我为啥我的标题这麽傻白甜 我也不知道,取名好难 路上看到可爱小植...

[Day4] 执行环境与执行堆叠

在昨天内容中可以知道,JavaScript 采用了静态作用域,函式在定义时就已经确定作用域,而在产生...

django新手村10-----关於注册

上一篇用到的注册,其实是有点小问题的,像是如果用户名重复了,或是帐号密码都不打也可以注册 这一篇比较...

[Day13] 文本/词表示方式(四)-共现矩阵与降维

ㄧ. 前言 前面有说明如何运用TFIDF与BOW来表示一个句子/文本的表示方式,但若以BOW这样的方...