数据结构与算法笔记:基础篇 - 分治算法:谈一谈大规模计算框架MapReduce中的分治思想

本文主要是介绍数据结构与算法笔记:基础篇 - 分治算法:谈一谈大规模计算框架MapReduce中的分治思想,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

MapReduce 是 Google 大数据处理的三姐马车之一,另外两个事 GFS 和 Bigtable。它在倒排索引、PageRank 计算、网页分析等搜索引擎相关的技术中都有大量的应用。

尽管开发一个 MapReduce 看起来很高深。实际上,万变不离其宗,它的本质就是本章要学的这种算法思想,分支算法。


如何理解分支算法?

为什么说 MapReduce 的本质就是分治算法呢?先来看看什么事分治算法?

分治算法(divide and conquer)的核心思想其实就是四个字,分而治之,也就是将原问题划分成 n 个规模较小,并且结构与原问题相似的子问题,递归地解决这些子问题,然后再合并其结果,就得到原问题的解。

这个定义看起来有点类似递归地定义。关于分治和递归,我们在 排序(下) 的时候讲过,分治算法是一种处理问题的思想,递归是一种编程技巧。实际上,分治算法一般都比较适合用递归来实现。分治算法的递归实现中,每一次递归都会涉及三个操作:

  • 分解:将原问题分解为一系列子问题;
  • 解决:递归地求解各个子问题,若子问题足够小,则直接求解;
  • 合并:将子问题的结果合并成原问题。

分治算法能解决的问题,一般要满足以下几个条件:

  • 原问题与分解成的小问题具有相同的模式;
  • 原问题分解的子问题可以独立解决,子问题之间没有相关性,这一点是分治算法跟动态规划的明显区别,等讲到动态规划,会详细对比这两种算法;
  • 具有分解终止条件,也就是说,当问题足够小时,可以直接求解;
  • 可以将子问题合并成原问题,而这个合并操作的复杂度不能太高,否则就起不到减少算法总体复杂度的效果了。

分治算法应用举例分析

理解分治算法的原理并不难,但是要想灵活应用并不容易。所以,接下来,我会带你用分治算法解决我们在讲排序的时候设计的一个问题,加深你对分治算法的理解。

还记得我们在排序算法里讲到的数据的有序度、逆序度的概念吗?我当时讲到,我们用有序度表示一组数据的有序程度,用逆序度表示一组数据的无序程度。

假设我们有 n 哥数据,我们期望数据从小到大排列,那完全有序的数据的有序度就是 n(n-1)/2,逆序度等于 0;相反,倒序排列的数据的有序度就是 0,逆序度就是 n(n-1)/2。除了这两种极端情况外,我们通过计算有序对或逆序对的个数,来表述数据的有序度或逆序度。

在这里插入图片描述

现在的问题是,如何编码求出一组数据的有序对个数或者逆序对个数呢? 因为有序对个数和逆序对个数的求解方式是类似的,所以你可以之思考逆序对个数的求解方法。

最笨的方法是,拿每个数组跟它后面的数字比较,看看有几个比它小。我们把比它小的数字的个数记作 k,通过这样的方式,把每个数字都考察一遍之后,然后对每个数字对应的 k 值求和,最后得到的总和就是逆序对个数。不过,这样擦做的时间复杂度是 O ( n 2 ) O(n^2) O(n2)。有没有更加高效的处理方法呢?

我们用分治法来试试。我们套用分治法的思想来求数组 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) {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[i++];}}while (i <= q) { // 处理剩下的tmp[k++] = a[i++];}while (j <= r) { // 处理剩下的tmp[k++] = a[j++];}for (i = 0; i <= r-p; i++) { // 从tmp拷贝回aa[p+i] = tmp[i];}}

很多同学经常会说,某某算法思想如此巧妙,我是怎么也想不到的。实际上,确实是的。有些算法确实非常巧妙,并不是每个人短时间都能想到的。比如这个问题,并不是每个人都能想到可以借助归并排序算法来解决。不夸张地说,如果之前没接触过,觉得部分人都想不到。但是,如果我告诉你可以借助归并排序算法来解决,那你就应该想到如何改造归并排序,来求解这个问题了,只要你能做到这一点,我觉得就很棒了。

关于分治算法,还有两道比较经典的问题:

  • 二维平面上有 n 个点,如何快速计算出两个距离最近的点对?
  • 有两个 nn 的矩阵 A,B,如何快速求解两个矩阵的成绩 C=AB?

分治思想在海量数据处理中的应用

分治算法思想的应用是非常广发的,并不仅限于指导编程和算法设计。它还经常用在海量数据处理的场景中。我们前面讲的数据结构和算法,大部分都是基于内存存储和单机处理。但是,如果要处理的数据量非常大,没法一次性放到内存中,这个时候,这些数据结构和算法就无法工作了。

比如,给 10GB 订单文件按照金额排序这样一个需求,看似是一个很简单的排序问题,但是因为数据量大,有 10GB,而我们机器的内存可能只有 2、3GB 这样子,无法一次性加载到内存,也就无法通过单纯地使用快排、归并排序等基础算法来解决了。

要解决这种数据量大到内存装不下的问题,我们就可以利用分治的思想。我们可以将海量的数据集合根据某种方法,划分为几个小的数据集合,每个小的数据集合单独加载到内存来解决,然后再将小数据集合合并成大数据集合。实际上,利用这种分治的处理思路,不仅仅能克服内存的限制,还能利用多线程或者多机处理,加快处理的速度。

比如刚刚的例子,给 10GB 的订单排序,我们可以先扫描一遍订单,根据订单的金额,将 10GB 的文件划分为几个金额区间。比如订单金额 1 到 100 元的放到一个小文件,101 到 200 的放到另一个文件,以此类推。这样每个小文件都可以单独加载到内存排序,最后将这些有序的小文件合并,就是最终有序的 10GB 订单数据了。

如果订单存储在类似 GFS 这样的分布式系统上,当 10GB 订单被划分成多个小文件的时候,每个文件可以并行加载到多态机器上处理,最后再将结果合并在一起,这样并行处理的速度也加快了很多。不过,这里有一点要注意,就是数据的存储与计算所在的机器是同一个或在网络中靠的很近(比如一个局域网内,数据存取速度很快),否则就会因为数据访问的速度,导致整个处理过程不但不会变快,反而有可能变慢。

你可能还有印象,这个就是我们在讲线性排序的时候举的例子。实际上,在前面已经学习的课程中,还讲了很多利用分治算法来解决的问题。

谈一谈大规模计算框架MapReduce中的分治思想

刚刚举的订单的例子,数据有 10GB 大小,可能给你的感受还不强烈。那如果我们要处理的数据时 1T、10T、100T 这样的数据,那一台机器处理的效率肯定是非常低的。而对于谷歌搜索引擎来说,网页爬取、清洗、分析、分词、计算权重、倒排索引等等各个环节中,都会面对如此海量的数据(比如网页)。所以利用集群并行处理显然是大势所趋。

一台机器过于低效,那我们就把人去拆分到多态机器上来处理。如果拆分之后的小人物之间互不干扰,独立计算,最后再将结果合并。这不就是分治思想吗?

实际上,MapReduce 框架只是一个任务调度器,底层依赖 GFS 来存储数据,依赖 Borg 管理机器。它从 GFS 中拿数据,交给 Brog 中的机器执行,并且时刻监控机器执行的进度,一旦机器出现宕机、进度卡壳等,就重新从 Brog 中调度一台机器执行。

尽管 MapReduce 的模型非常简单,但是在 Google 内部应用非常广泛。它除了可以用来处理这种数据与数据之间存在关系的任务,比如 MapReduce 的经典例子,统计文件中单词出现的频率。此外,它还可以用来处理数据与数据之间没有关系的任务,比如对网页分析、分词等,每个网页可独立的分析、分词,而这两个网页之间没有关系。网页几十亿、上百亿,如果单机处理,效率地下,我们就可以利用 MapReduce 提供可靠、高性能、高容错的并行计算框架,并行地处理这几十亿、上百亿的网页。

小结

本章讲了一种应用非常广泛的算法思想,分治算法。

分治算法用四个字概况就是 “分而治之”,将原问题划分成几个规模较小而结构与原问题相似的子问题,递归地解决这些子问题,然后再合并其结果,就得到原问题的解。这个思想非常简单、好理解。

本章讲解了两种分治算法的经典的应用场景,一个是用来指导编码,降低问题求解的时间复杂度,另一个是解决海量数据处理问题。比如 MapReduce 本质上就是利用了分治思想。

我们也时常感叹 Google 的创新能力如此之强,总是在引领技术的发展。实际上,创新并非离我们很远,创新的源泉来自对事物本质的认识。无数优秀架构设计的思想来源都是基础的数据结构和算法,这本身就是算法的一个魅力所在。

这篇关于数据结构与算法笔记:基础篇 - 分治算法:谈一谈大规模计算框架MapReduce中的分治思想的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1075210

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

hdu1496(用hash思想统计数目)

作为一个刚学hash的孩子,感觉这道题目很不错,灵活的运用的数组的下标。 解题步骤:如果用常规方法解,那么时间复杂度为O(n^4),肯定会超时,然后参考了网上的解题方法,将等式分成两个部分,a*x1^2+b*x2^2和c*x3^2+d*x4^2, 各自作为数组的下标,如果两部分相加为0,则满足等式; 代码如下: #include<iostream>#include<algorithm

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

poj 1113 凸包+简单几何计算

题意: 给N个平面上的点,现在要在离点外L米处建城墙,使得城墙把所有点都包含进去且城墙的长度最短。 解析: 韬哥出的某次训练赛上A出的第一道计算几何,算是大水题吧。 用convexhull算法把凸包求出来,然后加加减减就A了。 计算见下图: 好久没玩画图了啊好开心。 代码: #include <iostream>#include <cstdio>#inclu