分治算法的核心思想是将原问题划分成 n 个规模较小,并且结构与原问题相似的子问题,递归地解决这些子问题,然后再合并其结果,就得到原问题的解。分治算法是一种处理问题的思想,递归是一种编程技巧。分治算法比较适合使用递归来实现。
分治算法能解决的问题,一般需要满足下面这几个条件:
-
原问题与分解成的小问题具有相同的模式;
-
原问题分解成的子问题可以独立求解,子问题之间没有相关性,这一点是分治算法跟动态规划的明显区别;
-
具有分解终止条件,也就是说,当问题足够小时,可以直接求解;
-
可以将子问题合并成原问题,而这个合并操作的复杂度不能太高,否则就起不到减小算法总体复杂度的效果了。
分治算法应用举例分析
分析排序时,可以使用有序度和逆序度来分析排序算法的时间复杂度,计算有序度需要找出序列中有序对个数,现在问题是,如何编程求出一组数据的有序对个数或者逆序对个数?
最简单的方式是穷举,这样操作的时间复杂度是,我们可以使用分治算法来解决。可以将数组分为前后两半A1和A2,分别计算A1和A2的逆序对个数K1和K2,然后再计算A1与A2之间的逆序对个数K3。那么数组A的逆序对个数就等于K1+K2+K3。
使用分治算法的其中一个要求是,子问题合并代价不能太大,否则不能起到降低时间复杂度的效果,那么如何快速计算两个子问题A1和A2之间的逆序对个数?
在归并排序中一个非常关键的步骤是将两个有序的小数组合并成一个有序的数组。其实在合并的过程中,可以计算两个小数组之间的逆序对个数。每次合并操作都可以计算逆序对个数,将这些逆序对个数求和即可得到这个数组的逆序对个数。借用原文的图片来说明:
关于分治算法的两个比较经典的问题:
- 二维平面上有n个点,如何快速计算两个距离最近的点对?
- 有两个n * n的矩阵A,B,如何快速求解两个矩阵的乘积C=A * B。
分治思想在海量数据处理中的应用
大部分数据结构与算法是基于内存存储与单机处理。但是如果数据量过大将无法一次性放入内存。比如给10TB订单文件按照金额排序这样的需求,看似是一个简单的排序问题,但是由于数据量大,无法一次性将数据加载到内存,也就无法通过单纯的使用快排、归并等基础算法来解决了。
我们可以使用分治思想,将海量的数据集合根据某种方法,划分为几个小的数据集合,每个小的数据集合单独加载到内存来解决,然后再将小数据集合合并成大的数据集合。这种分治处理的思路,不仅能客服内存的限制,还能利用多线程或者多机处理,加快处理的速度。不过需要注意,数据的存储与计算所在的机器是同一个或者在网络中靠的很近(比如一个局域网内,数据存取速度很快),否则就会因为数据访问的速度,导致整个处理过程不但不会变快,反而有可能变慢。
为什么说MapReduce的本质就是分治思想?
对于谷歌搜索引擎来说,网页爬取、清洗、分析、粉刺、计算权重、倒排序等等各个环节中,都会面对海量的数据,所以利用集群并行处理是大势所趋。一台机器过于低效,将任务拆分到多台机器上来处理,如果各个小任务之间不互相干扰,独立计算,最后将结果合并,这就是分治思想。