分而治之:据不同的成因选择不同的解决方案。
成语大全如是说。而似乎分治只借了这个成语的名,意思却偏向于问题的拆解再合并,就是把一个复杂的问题分解成多个相同或相似的子问题,再把子问题分解成更小的问题。这种分解问题的思维,。这里先埋一个雷:MapReduce,游鱼过会再提。
从算法开始
No.1 经典排序算法——快速排序
当有人问起:你知道哪几种排序算法啊?大多说人不加思索都能说出三种:冒泡排序(你要是说你只知道选择和插入。。。我估计也不太可能)、选择排序、插入排序。快速排序就是对冒泡排序的一次改进。
冒泡排序是两两比较,当两两不符合当前规定的顺序时,则交换两者的位置。而快速排序就是选定一个基准值,假设从小到大,基准值左边的如果比基准值大就放到右边,基准值左边的如果比基准值小就放到右边,然后在左右两边再循环这个过程。
如下图所示:
快速排序
算法:(备注,相对于图片局部优化了,减少了交换的次数)
#!usr/bin/c++
#include <iostream>
using namespace std;
#需要快排的数组a,数组第一位low,数组最后一位high。
void Qsort(int a[], int low, int high)
{
#初始数组第一位low>=数组最后一位high,说明这一层排序已经到了最小的字元:数组内单个元素。
if(low >= high)
{
return;
}
int first = low;
int last = high;
int key = a[first];/*用数组的第一个记录作为枢轴*/
while(first < last)
{
while(first < last && a[last] >= key)
{
--last;
}
a[first] = a[last];/*将比第一个小的移到低端*/
while(first < last && a[first] <= key)
{
++first;
}
a[last] = a[first];/*将比第一个大的移到高端*/
}
a[first] = key;/*枢轴保存到first处,此时因为first一路走来保证first前面的小于key,last一路走来保证last后面的大于key,且直到最后first遇到了last*/
/*对第一位到枢纽的前一位进行二次快排*/
Qsort(a, low, first-1);
/*对最后一位到枢纽的后一位进行二次快排*/
Qsort(a, first+1, high);
}
int main()
{
int a[] = {57, 68, 59, 52, 72, 28, 96, 33, 24};
Qsort(a, 0, sizeof(a) / sizeof(a[0]) - 1);
for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
cout << a[i] << "";
}
return 0;
}/*参考数据结构p274(清华大学出版社,严蔚敏)*/
至于对经典快速排序算法的再优化,鱼就不说了,爱看看吧,十分杂乱且刺激。
No.2 经典排序算法——归并排序
快排是分治法从全局有序化(左边小右边大)逐步局部有序化(小的是唯一一个,大的也是唯一一个,那这三个数不就排好序了吗)的排序,归并则是从局部有序化(从两个数开始排好序)逐步全局有序化(全部都排好序)的过程。
因为局部是有序的,所以归并排序是用的以下的手法:
归并排序
#!/usr/bin/python
def MergeSort(lists):
if len(lists) <= 1:
return lists
num = int( len(lists) / 2 )
left = MergeSort(lists[:num])
right = MergeSort(lists[num:])
return Merge(left, right)
def Merge(left,right):
r, l=0, 0
result=[]
while l<len(left) and r<len(right):
if left[l] < right[r]:
result.append(left[l])
l += 1
else:
result.append(right[r])
r += 1
result += list(left[l:])
result += list(right[r:])
return result
print(MergeSort([1, 2, 3, 4, 5, 6, 7, 90, 21, 23, 45]))
#from 百科,不得不说这些写百科的也真的是随手,歪门邪道的错误
经过函数式的洗礼
绝大多数的高级语言都带入了Map(映射)函数、Reduce(归约)函数的理念
#!/usr/bin/js
//array.map(function(currentValue,index,arr), thisValue)
//currentValue=>当前元素的值,index=>当前元素的索引,arr=>当前数组对象,thisValue=>作为执行回调使用
//例子
var numbers = [4, 9, 16, 25];
function myFunction() {
x = document.getElementById("demo")
x.innerHTML = numbers.map(Math.sqrt);
}
//from runoob
#!/usr/bin/js
//array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
//比Map多了total作为归并后的返回值。
//例子
var numbers = [65, 44, 12, 4];
function getSum(total, num) {
return total + num;
}
function myFunction(item) {
document.getElementById("demo").innerHTML = numbers.reduce(getSum);
}
//from runoob
先回到最一开始归并排序的逻辑上去——可以看到MergeSort()是在用递归分解问题,即一个大问题分解成一个小问题。而Merge则是在归并,把有序的小问题合并成一个有序的大问题,因为小问题是有序的,所以排序成本极低(小于n)。由此对比下Map和Reduce的函数逻辑,尽管排序少了对小问题的Map映射处理,也是实现了Reduce的思想进行了归约,假设现在有一个一百万无序的数组,正常的排序算法时间复杂度都在nlgn~n^2之间不定,如果用一台电脑去处理,可能要几个小时的时间。而归并排序在Reduce的过程中成本极低,则可以采用分布式处理的方式,先把问题Map出去,交给服务器集群进行处理,最后再Reduce回来得到最后的结果。
对,就是这样分治策略是分布式系统的逻辑基础,现如今大多数分布式处理都来源于MapReduce思想,由此游鱼还认识了Lisp语言。
在MapReduce里,Map处理的是原始数据,自然是杂乱无章的,每条数据之间互相没有关系;到了Reduce阶段,数据是以key后面跟着若干个value来组织的,这些value有相关性,至少它们都在一个key下面,于是就符合函数式语言里map和reduce的基本思想了。去借用MapReduce模型的话,开发人员只需要考虑如何进行数据处理,提取特征,最后再考虑如何归纳特征。开发人员不用考虑如何并发的问题。
MapReduce
落地到编程模型
分布式存储HDFS——分布式处理MapReduce
这方面鱼推荐大家多去阅读文献,不献丑了。只大概说说,复制一下个人之前Apache Storm的笔记
Apache Storm
一 优势
- Apache 产品通用优势:可以通过线性增加资源来保持性能
- 适用性、语言普适性、分布式处理快的优势、操作智能
- 优秀的防呆措施
二、理念
理念Input data source: 数据源
Spout: 流的源,通过编写 Spout 以从数据源读取数据 Bolt:逻辑处理单元, Spout 将数据传递到 Bolt 和 Bolt过程,并产生新的数据流。
Bolt 可以拍行过滤、聚合、加入,并与数据 源和数据库进行交互
Tuple:有序元素的列表
Stream:元组的无序序列
关键词:拓扑和路由
三、集群架构
集群架构Apache Storm 的集群架构,采用了主从设备模式。这种模式参照于?
Zookeeper framework:协助 Supervisor 和 nimbus 交互
Nimbus : Storm 集群的主节点,又称Master。而工作结点分发数据并监控故障
Supervisor:工作节点又称 Workers,负责管理工作进程以完成分配的任务
Worker process:工作进程,其执行与特定拓扑相关的任务。其创建执行器,并要求它们完成任务
Execute:执行器。由工作进程产生的单个线程,用于特定的spout与 bolt
Task:任务,实际执行数据处理。它是个 spout 或 bolt
网友评论