分而治之算法–为什么不分成两个以上的部分?


33

在快速排序和合并排序等分而治之算法中,通常将输入(至少在介绍性文本中)分成两部分,然后递归处理两个较小的数据集。对我来说,如果这两个部分花费的时间少于处理整个数据集的一半,这将使解决问题更快。但是,为什么不将数据集分为三部分呢?四个?n

我猜想将数据拆分为许多子集的工作不值得,但是我缺乏直觉,无法看到一个应该在两个子集处停止。

我也看到了很多有关3向快速排序的参考。什么时候更快?实际使用什么?


尝试创建类似于quicksort的算法,该算法将数组分为三个部分。
gnasher729 '16

Answers:


49

对我来说,如果这两个部分花费的时间少于处理整个数据集的一半,这将使解决问题更快。

不是分而治之算法的本质。通常,关键是算法根本无法“处理整个数据集”。取而代之的是,将其分为难以解决的部分(例如,对两个数字进行排序),然后对其进行简单的解决,并将结果重新组合在一起,以得出完整数据集的解决方案。

但是,为什么不将数据集分为三部分呢?四个?n?

主要是因为将其拆分为两个以上的部分并重组两个以上的结果会导致实现更复杂,但不会改变算法的基本(大O)特征-差异是一个常数,可能会导致速度降低如果两个以上子集的划分和重组产生了额外的开销。

例如,如果您进行三向合并排序,那么在重组阶段,您现在必须为每个元素找到3个元素中的最大元素,这需要2个比较而不是1个比较,因此您将进行总共两倍的比较。作为交换,您将递归深度减少了ln(2)/ ln(3)== 0.63,因此交换减少了37%,但是比较(和内存访问)增加了2 * 0.63 == 26%。这是好是坏,取决于硬件中哪个更昂贵。

我也看到了很多有关3向快速排序的参考。什么时候更快?

显然可以证明快速排序双数据透视变体需要相同数量的比较,但平均交换次数减少了20%,所以这是净收益。

实际使用什么?

如今,几乎没有人再编程自己的排序算法了。他们使用图书馆提供的书。例如,Java 7 API实际上使用双轴快速排序。

实际上出于某种原因自己编写排序算法的人会倾向于使用简单的2向变体,因为在大多数情况下,出错的可能性较小,而性能却提高了20%。请记住:迄今为止,最重要的性能改进是代码从“不工作”变为“工作”。


1
小提示:Java 7 在对基元进行排序时才使用Dual-Pivot快速排序。要对对象排序,请使用Timsort。
巴库里

1
+1表示“现在,几乎没有人再编写自己的排序算法了”,(更重要的是)“记住:到目前为止,最重要的性能改进是代码从“不工作”变为“工作”。但是,我想知道,例如,如果将数据集分为许多部分,那么开销是否仍然微不足道。由于恰巧,所以有其他人: bealto.com/gpu-sorting_intro.html stackoverflow.com/questions/1415679/... devgurus.amd.com/thread/157159
AndrewJacksonZA

我有点儿慢。谁能解释为什么还要进行2 * 0.69的比较?不确定0.69的来源。
jeebface '16

@jeebface糟糕,这是一个错字(现已修复)。它是0.63(递归深度的减少),那么结果也增加了26%。
Michael Borgwardt

30

渐近地说,没关系。例如,二进制搜索进行大约log 2  n比较,而三进制搜索进行大约log 3  n比较。如果您知道对数,则知道log a  x = log b  x / log b  a,因此二进制搜索仅使1 / log 3 3倍比较的2≈1.5倍。这也是没有人以大Oh标记指定对数底数的原因:无论给定实际底数是多少,它始终是与给定底数对数一致的常数。因此,将问题分成更多的子集并不会提高时间复杂度,并且实际上不足以抵消更复杂的逻辑。实际上,这种复杂性可能会对实际性能产生负面影响,从而增加缓存压力或使微优化变得不可行。

另一方面,某些树状数据结构确实使用了较高的分支因子(远大于3,通常为32或更大),尽管通常是出于其他原因。它提高了内存层次结构的利用率:存储在RAM中的数据结构更好地利用了缓存,存储在磁盘上的数据结构需要较少的读取HDD-> RAM。


是的,请查找八叉树,以了解二叉树结构以外的特定应用程序。
daaxix

@daaxix btree可能更常见。
Jules

4

有一些搜索/排序算法不除以2,而是除以N。

一个简单的示例是通过哈希编码进行搜索,这需要O(1)时间。

如果哈希函数是保留顺序的,则可以将其用于制定O(N)排序算法。(您可以将任何排序算法都视为仅执行N次搜索数字应在结果中的位置。)

根本的问题是,当程序检查某些数据然后进入某些跟随状态时,存在多少跟随状态,其概率接近于相等的程度?

当计算机对两个数字进行比较(例如,然后比较是否跳跃)时,如果两条路径的可能性相同,则程序计数器会“知道”每条路径上的另一条信息,因此平均而言,它已“获悉”一个信息一点。如果一个问题需要学习M位,那么使用二进制决策就不能在少于M个决策的情况下得到答案。因此,例如,在1024大小的排序表中查找数字不能只用少于10个二元决策来完成,这仅仅是因为少做就没有足够的结果,但是肯定可以用更多的结果来完成。

当计算机获取一个数字并将其转换为数组的索引时,它会“学习”到数组中元素数量的对数以2为底,并在固定时间内进行操作。例如,如果存在一个包含1024个条目的跳转表,那么跳转表或多或少地具有同等的可能性,那么跳转到该表将“学习” 10位。这是哈希编码背后的基本技巧。排序示例就是如何对一副纸牌进行排序。有52个垃圾桶,每个垃圾桶一个。将每张卡放入其存储箱中,然后将它们全部sc起。无需细分。


1

由于这是一个关于一般分而治之的问题,而不仅仅是排序,我很惊讶没有人提出大师定理

简而言之,分而治之算法的运行时间由两个抵消力量决定:从将大问题转变为小问题所获得的收益,以及解决更多问题所付出的代价。根据算法的细节,将一个问题分成两个以上的部分可能会也可能不会,也可能不会。如果在每个步骤中划分为相同数量的子问题,并且知道在每个步骤中组合结果的时间复杂度,则主定理将告诉您整个算法的时间复杂度。

Karatsuba算法乘法采用了3路分而治之达到其击败为O(n ^ 2)为普通乘法算法(n是数字的数量在将O的运行时间(3 N ^ log_2 3)数字)。


在Master定理中,您创建的子问题的数量不是唯一的因素。在Karatsuba及其表亲Strassen中,改进实际上来自智能地合并了一些子问题的解决方案,因此您减少了对子问题的递归调用的数量。简而言之,b掌握中定理的上升要求a上升较慢,以便您进一步细分。
通知2014年

-4

由于它的二进制性质,计算机非常有效地将事物除以2,而不是将其除以3。因此,首先将2除以1,然后再将其中之一除以2,就可以得到3的除数。因此,如果需要除用2除以得到3除法,也可以除以2。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.