分治策略——演算法
分而治之:據不同的成因選擇不同的解決方案。
成語大全如是說。而似乎分治只借了這個成語的名,意思卻偏向於問題的拆解再合併,就是把一個複雜的問題分解成多個相同或相似的子問題,再把子問題分解成更小的問題。這種分解問題的思維,。這裡先埋一個雷: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
四、工作流程

工作流程