分治算法:谈一谈大规模计算框架MapReduce中的分治思想
MapReduce是Google大数据处理的三驾马车之一,另外两个是GFS和Bigtable
理解分治算法:
将原问题划分成n个规模较小,并且结构与原问题相似的子问题,递归的解决这些子问题,然后再合并其结果得到原问题的解
分治算法是一种处理问题的思想,递归是一种编程技巧,分治算法的递归实现中,每一层递归都会涉及这样三个操作:
- 分解:将原问题分解成一系列子问题
- 解决:递归解决各个子问题,若子问题足够小,直接求解
- 合并:将子问题的结果合并成原问题
分治算法应用举例分析
用有序度表示一组 数据的有序程度,逆序度表示一组数据的无序程度。
假设我们有n个数据,期望数据从小到大排列,那么完全有序的数据的有序度就是n(n-1)/2,逆序度是0,相反,倒序排列的数据的有序度是0,逆序度是n(n-1)/2
2,4,3,1,5,6 逆序对个数:4
(2,1) (4,3) (4.1) (3,1)
如何编程求出一组数据的有序对个数或者逆序对个数?
套用分治算法思想求数组A的逆序对个数,将数组分成前后两半A1 A2,分别计算A1 A2的逆序对个数K1 K2,然后再计算A1 A2的逆序对个数K3,数组A的逆序对个数=K1+K2+K3
如何快速计算出两个子问题A1 A2之间的逆序对个数呢?
归并排序有一个操作是将两个有序的小数组合并成一个有序的数组,实际上,这个合并过程中,可以计算这两个小数组的逆序对个数。每次合并操作,都计算逆序对个数,把计算出来的逆序对个数求和,然后就是数组的逆序对个数。
private int num = 0 ; //全局变量或者成员变量
public int count (int[] a ,int n){
num = 0 ;
mergeSortCounting(a , 0 ,n-1);
return num;
}
private void mergeSortCounting(int[] a ,int p ,int r){
if (p >= r) return;
int q = (p+r) / 2;
mergeSortCounting(a,p,q);
mergeSortCounting(a , q+1 ,r);
merge(a , p , q , r);
}
private void merge(int[] a ,int p ,int q, int r){ //可以改成merge(int[] a ,int low ,int middle, int high)
int i = p , j = q+1 , k = 0 ;
int[] tmp = new int[r-p+1];
while(i<=q && j <=r){
if(a[i] <= a[j]){
tmp[k++] = a[i++];
}else{
num += (q-i+1); // 统计p-q之间,比a[j]大的元素个数
tmp[k++] = a[j++];
}
}
while (i <= q){ //处理剩下的
tmp[k++] = a[i++];
}
while (j <= r){ //处理剩下的
tmp[k++] = a[j++];
}
for(i = 0 ; i <= r-p ;++i){ //从tmp拷贝回a
a[p+i] = tmp[i];
}
}
关于分治算法的经典问题:
- 二维平面上有n个点,如何快速计算出两个距离最近的点对?
- 有两个n
*
n的矩阵A,B,如何快速求解出两个矩阵的乘积C=A*
B?
分治思想在海量数据处理中的应用
解决数据量大到内存装不下的问题,利用分治思想,将海量的数据集合根据某种方法划分为几个小的数据集合,每个小的数据集合单独加载到内存中解决,然后将小数据集合合并成大数据集合,不仅能克服内存限制还利用多线程或多处理机处理,加快速度
比如给10GB的订单排序,可以先扫描一遍订单,根据金额,将10GB文件划分为几个金额区间,比如1~100 101~200之间的放到另一个文件,依此类推,每个小文件都可以单独加载到内存排序,最后将有序的小文件合并,就是最后的有序订单数据
为什么说MapReduce的本质是分治思想?
如果要处理的数据是1T,10T,100T,所以利用集群并行处理是大势所趋
MapReduce框架只是一个任务调度器,除了用来处理数据与数据之间存在关系任务,还可以统计文件中单词出现的频率,还有处理没有关机的人雾,比如对网页分析,分词等,