BFPRT算法(中位数之中位数)初窥 五

简介: BFPRT算法的作者是5位真正的大牛(Blum 、 Floyd 、 Pratt 、 Rivest 、 Tarjan),该算法入选了在StackExchange上进行的当今世界十大经典算法,而算法的简单和巧妙颇有我们需要借鉴学习之处。

BFPRT算法的作者是5位真正的大牛(Blum 、 Floyd 、 Pratt 、 Rivest 、 Tarjan),该算法入选了在StackExchange上进行的当今世界十大经典算法,而算法的简单和巧妙颇有我们需要借鉴学习之处。

BFPRT解决的问题十分经典,即从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分析,BFPRT可以保证在最坏情况下仍为线性时间复杂度。

当我们面对这一问题时,首先想到的直观方法一般为k次(假设k<n 2)选择排序,方法的伪码如下
function select(list[1..n], k)
     for i from 1 to k
         minIndex = i
         minValue = list[i]
         for j from i+1 to n
             if list[j] < minValue
                 minIndex = j
                 minValue = list[j]
         swap (list[i],list[minIndex])
     return list[k]

通过k次循环,方法可以依次选择出最小的k个值,该方法时间复杂度为O(kn)。当k较小时,方法的效率较为优秀,但当k->n/2时,方法复杂度变为了O(n^2)

思考该方法中多余的能量支出,方法按顺序输出了最小的k个元素,而这并不是我们需要的,如果我们只获得哪些值比该值小,而不对比其小的进行排序,算法代价将大幅下降。由于上面的方法用了选择排序的思想,那么利用快速排序的思想进行选择容易想到quickselection。
每次选择某一pivot,通过快速排序的思路,我们可以获得比pivot小的所有数和比其大的所有数,由此可以选出所需的kth值在哪以区间呢,并在该区间内再次使用quickselection。方法的伪码如下
function select(list, left, right, k)
     if left = right // If the list contains only one element
         return list[left]  // Return that element
     select pivotIndex between left and right
     pivotNewIndex := partition(list, left, right, pivotIndex)
     pivotDist := pivotNewIndex – left + 1
     if pivotDist = k
         return list[pivotNewIndex]
     else if k < pivotDist
         return select(list, left, pivotNewIndex - 1, k)
     else
         return select(list, pivotNewIndex + 1, right, k - pivotDist)

如quicksort一样,该方法在实际应用中有较好的效果,但在某些特殊情况中,由于pivot的选择,会出现一些效率极端不好的情况,例如某倒排表。

BFPRT是一种获得较优秀pivot的方法,方法的思路是使获得的pivot能够较为有效的对整个数据进行分割,并在其中利用寄存器的快速计算能力将问题拆分为代价极小的子问题。
方法的思路为:将元组分为n/5个5元的小数组,并对每组求中位数,在长度为n/5的序列中,求其中位数,该中位数的中位数保证了至少30%的数据在其一侧,由此保证了pivot的有效性(如图,改图来自wikipedia)
中位数之中位数
关于为何利用5作为小元组大小,我的想法是与寄存器的数量和运算有关。
由于pivot的有效分割和5元组中位数易求性,从n元组中取值的代价T(n)<=T(n/5)+T(7n/10)+O(n),T(n/5)是为中位数取中位数的时间,O(n)是遍历序列并求得中位数数列的时间.
设T(n)=cn,此处c可以不是常熟,若c与n成线性关系,则T(n)=O(n^2),设遍历时间为an,a为常数
则有 T(n)<=c(n/5)+c(7n/10)+an=c(9/10*n)+an //此处,低次已被省略低次项
求得C<=10a 故c为常数,与n无关
且T(n)至少为O(n),
综上,该算法为一线性算法

相关文章
|
6月前
|
算法 测试技术 C++
【动态规划】【中位数】【C++算法】1478. 安排邮筒
【动态规划】【中位数】【C++算法】1478. 安排邮筒
|
11月前
|
算法 测试技术 C#
C++前缀和算法的应用:统计中位数为 K 的子数组
C++前缀和算法的应用:统计中位数为 K 的子数组
|
6月前
|
算法
【MATLAB】语音信号识别与处理:移动中位数滤波算法去噪及谱相减算法呈现频谱
【MATLAB】语音信号识别与处理:移动中位数滤波算法去噪及谱相减算法呈现频谱
89 2
|
6月前
|
算法
LeetCode算法题---无重复字符的最长子串、寻找两个正序数组的中位数(三)
LeetCode算法题---无重复字符的最长子串、寻找两个正序数组的中位数(三)
63 0
|
算法 C++
剑指offer(C++)-JZ41:数据流中的中位数(算法-排序)
剑指offer(C++)-JZ41:数据流中的中位数(算法-排序)
剑指offer(C++)-JZ41:数据流中的中位数(算法-排序)
|
4月前
|
人工智能 算法
算法金 | 平均数、众数、中位数、极差、方差,标准差、频数、频率 一“统”江湖
**统计学江湖概要** - **平均数(均值)**:数字的总和除以数量,代表集中趋势,如分赃时平均分配。 - **众数**:出现次数最多的数字,反映了最常见的值,如同一招式被频繁使用。 - **中位数**:排序后位于中间的值,反映数据的中心位置,如同武者武功的中等水平。 - **极差**:最大值减最小值,表示数据波动范围,类似武功最高与最低的差距。 - **方差**:衡量数据波动性,计算每个数值与均值差的平方和的平均数。 - **标准差**:方差的平方根,同单位的波动度量。 - **频数**:某个值出现的次数,如统计武器使用情况。 - **频率**:频数与总次数的比例,显示出现的相对频率。
86 2
算法金 | 平均数、众数、中位数、极差、方差,标准差、频数、频率 一“统”江湖
|
6月前
|
搜索推荐 算法 索引
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
159 4
|
6月前
|
算法 安全 C#
Leetcode算法系列| 4. 寻找两个正序数组的中位数
Leetcode算法系列| 4. 寻找两个正序数组的中位数
|
算法 测试技术 C#
C++算法:数据流的中位数
C++算法:数据流的中位数
|
算法 测试技术 C++
C++算法:寻找两个正序数组的中位数
C++算法:寻找两个正序数组的中位数