1. 哪些演算法可以應用於大數據挖掘
基本上傳統
數據挖掘
中的演算法都可以應用,只是在大數據挖掘時需要額外考慮演算法復雜性對於數據量的關系,如果是呈指數之類的關系,就不能應用了。
2. 信息發展從而衍生各種數據演算法,大數據又是如何運用在我們的生活中呢
網路時代大多都是依靠各種數據演算法而運行的,也有不少的數據演算法是從發展中不斷衍生的,大家最為熟悉的就是大數據。人人都處於大數據時代,只要使用網路必然就接觸過大數據,因為它實際上就滲透在我們生活的每個角落。隨著信息發展從而衍生了各種數據演算法,那麼大數據又是如何運用在我們的生活中呢?
當我們在使用各種軟體的時候,其實就是在被試探,刷視頻時長時間停留在某個視頻,購物時經常查看某個價格區間的物品,那麼下次打開軟體時推送的就會依照上一次的使用習慣進行推送。所以大數據時代為人們增添了不少便利,更是成為了大家的及時雨。
3. 大數據時代:如何巧用演算法思維,改善日常生活,警惕認知陷阱嗎
主人公艾迪·莫萊(布萊德利·庫珀 Bradley Cooper 飾)是一個缺乏靈感,潦倒頹廢的作家。許久不聯系的前小舅子給他一種正處在試驗階段的葯物--NZT。服用一次之後艾迪發現自己精力充沛,思維清晰,行動敏捷,甚至能在瞬間把一些平時不易留意的細節和信息重新組合和整理並得出推論——這款名為NZT的新葯物能使人發揮大腦100%的能力。藉助NZT,艾迪重獲靈感並進入上流社會,但大獲成功之餘也有致命的作用:身體不適和情緒失控。而此時埃迪已不能離開這種葯,這使他進入了一個黑暗領域,而來自神秘勢力的殺手也在暗處監視他……
4. 大數據和編程,有什麼關系
Java是一門編程語言,實現同一個需求有上百種編程語言可以完成,Java之於大數據,就是一種工具罷了。
大數據就是一個行業,實現同一個需求同樣有多種工具可以選擇,狹義一點以技術的角度講,各類框架有Hadoop,spark,storm,flink等,就這類技術生態圈來講,還有各種中間件如flume,kafka,sqoop等等 ,這些框架以及工具大多數是用Java編寫而成,但提供諸如Java,scala,Python,R等各種語言API供編程。
所以,大數據的實習需要用到Java,但是Java並不是大數據。
5. 大數據可以應用在哪些方面
可以應用在雲計算方面。
大數據具體的應用:
1、洛杉磯警察局和加利福尼亞大學合作利用大數據預測犯罪的發生。
2、google流感趨勢(Google Flu Trends)利用搜索關鍵詞預測禽流感的散布。
3、統計學家內特.西爾弗(Nate Silver)利用大數據預測2012美國選舉結果。
4、麻省理工學院利用手機定位數據和交通數據建立城市規劃。
5、梅西百貨的實時定價機制。根據需求和庫存的情況,該公司基於SAS的系統對多達7300萬種貨品進行實時調價。
6、醫療行業早就遇到了海量數據和非結構化數據的挑戰,而近年來很多國家都在積極推進醫療信息化發展,這使得很多醫療機構有資金來做大數據分析。
7、及時解析故障、問題和缺陷的根源,每年可能為企業節省數十億美元。
8、為成千上萬的快遞車輛規劃實時交通路線,躲避擁堵。
9、分析所有SKU,以利潤最大化為目標來定價和清理庫存。
10、根據客戶的購買習慣,為其推送他可能感興趣的優惠信息。
大數據的用處:
1、與雲計算的深度結合。大數據離不開雲處理,雲處理為大數據提供了彈性可拓展的基礎設備,是產生大數據的平台之一。
自2013年開始,大數據技術已開始和雲計算技術緊密結合,預計未來兩者關系將更為密切。除此之外,物聯網、移動互聯網等新興計算形態,也將一齊助力大數據革命,讓大數據營銷發揮出更大的影響力。
2、科學理論的突破。隨著大數據的快速發展,就像計算機和互聯網一樣,大數據很有可能是新一輪的技術革命。可能會改變數據世界裡的很多演算法和基礎理論,實現科學技術上的突破。
網路--大數據
6. 大數據思維認為海量數據結合復雜演算法在應用中更加有效.對嗎
建議樓主可以下一個FineBI試一試。大數據技術的戰略意義不在於掌握龐大的數據信息,而在於對這些含有意義的數據進行專業化處理。換言之,如果把大數據比作一種產業,那麼這種產業實現盈利的關鍵,在於提高對數據的「加工能力」,通過「加工」實現數據的「增值」。
7. PHP的演算法可以實現大數據分析嗎
1.Bloom filter
適用范圍:可以用來實現數據字典,進行數據的判重,或者集合求交集
基本原理及要點:
對於原理來說很簡單,位數組+k個獨立hash函數。將hash函數對應的值的位數組置1,查找時如果發現所有hash函數對應位都是1說明存在,很明顯這個過程並不保證查找的結果是100%正確的。同時也不支持刪除一個已經插入的關鍵字,因為該關鍵字對應的位會牽動到其他的關鍵字。所以一個簡單的改進就是 counting Bloom filter,用一個counter數組代替位數組,就可以支持刪除了。
還有一個比較重要的問題,如何根據輸入元素個數n,確定位數組m的大小及hash函數個數。當hash函數個數k=(ln2)*(m/n)時錯誤率最小。在錯誤率不大於E的情況下,m至少要等於n*lg(1/E)才能表示任意n個元素的集合。但m還應該更大些,因為還要保證bit數組里至少一半為 0,則m 應該>=nlg(1/E)*lge 大概就是nlg(1/E)1.44倍(lg表示以2為底的對數)。
舉個例子我們假設錯誤率為0.01,則此時m應大概是n的13倍。這樣k大概是8個。
注意這里m與n的單位不同,m是bit為單位,而n則是以元素個數為單位(准確的說是不同元素的個數)。通常單個元素的長度都是有很多bit的。所以使用bloom filter內存上通常都是節省的。
擴展:
Bloom filter將集合中的元素映射到位數組中,用k(k為哈希函數個數)個映射位是否全1表示元素在不在這個集合中。Counting bloom filter(CBF)將位數組中的每一位擴展為一個counter,從而支持了元素的刪除操作。Spectral Bloom Filter(SBF)將其與集合元素的出現次數關聯。SBF採用counter中的最小值來近似表示元素的出現頻率。
問題實例:給你A,B兩個文件,各存放50億條URL,每條URL佔用64位元組,內存限制是4G,讓你找出A,B文件共同的URL。如果是三個乃至n個文件呢?
根據這個問題我們來計算下內存的佔用,4G=2^32大概是40億*8大概是340億,n=50億,如果按出錯率0.01算需要的大概是650億個 bit。現在可用的是340億,相差並不多,這樣可能會使出錯率上升些。另外如果這些urlip是一一對應的,就可以轉換成ip,則大大簡單了。
2.Hashing
適用范圍:快速查找,刪除的基本數據結構,通常需要總數據量可以放入內存
基本原理及要點:
hash函數選擇,針對字元串,整數,排列,具體相應的hash方法。
碰撞處理,一種是open hashing,也稱為拉鏈法;另一種就是closed hashing,也稱開地址法,opened addressing。 (http://www.my400800.cn)
擴展:
d-left hashing中的d是多個的意思,我們先簡化這個問題,看一看2-left hashing。2-left hashing指的是將一個哈希表分成長度相等的兩半,分別叫做T1和T2,給T1和T2分別配備一個哈希函數,h1和h2。在存儲一個新的key時,同時用兩個哈希函數進行計算,得出兩個地址h1[key]和h2[key]。這時需要檢查T1中的h1[key]位置和T2中的h2[key]位置,哪一個位置已經存儲的(有碰撞的)key比較多,然後將新key存儲在負載少的位置。如果兩邊一樣多,比如兩個位置都為空或者都存儲了一個key,就把新key 存儲在左邊的T1子表中,2-left也由此而來。在查找一個key時,必須進行兩次hash,同時查找兩個位置。
問題實例:
1).海量日誌數據,提取出某日訪問網路次數最多的那個IP。
IP的數目還是有限的,最多2^32個,所以可以考慮使用hash將ip直接存入內存,然後進行統計。
3.bit-map
適用范圍:可進行數據的快速查找,判重,刪除,一般來說數據范圍是int的10倍以下
基本原理及要點:使用bit數組來表示某些元素是否存在,比如8位電話號碼
擴展:bloom filter可以看做是對bit-map的擴展
問題實例:
1)已知某個文件內包含一些電話號碼,每個號碼為8位數字,統計不同號碼的個數。
8位最多99 999 999,大概需要99m個bit,大概10幾m位元組的內存即可。
2)2.5億個整數中找出不重復的整數的個數,內存空間不足以容納這2.5億個整數。
將bit-map擴展一下,用2bit表示一個數即可,0表示未出現,1表示出現一次,2表示出現2次及以上。或者我們不用2bit來進行表示,我們用兩個bit-map即可模擬實現這個2bit-map。
4.堆
適用范圍:海量數據前n大,並且n比較小,堆可以放入內存
基本原理及要點:最大堆求前n小,最小堆求前n大。方法,比如求前n小,我們比較當前元素與最大堆里的最大元素,如果它小於最大元素,則應該替換那個最大元素。這樣最後得到的n個元素就是最小的n個。適合大數據量,求前n小,n的大小比較小的情況,這樣可以掃描一遍即可得到所有的前n元素,效率很高。
擴展:雙堆,一個最大堆與一個最小堆結合,可以用來維護中位數。
問題實例:
1)100w個數中找最大的前100個數。
用一個100個元素大小的最小堆即可。
5.雙層桶劃分 ----其實本質上就是【分而治之】的思想,重在「分」的技巧上!
適用范圍:第k大,中位數,不重復或重復的數字
基本原理及要點:因為元素范圍很大,不能利用直接定址表,所以通過多次劃分,逐步確定范圍,然後最後在一個可以接受的范圍內進行。可以通過多次縮小,雙層只是一個例子。
擴展:
問題實例:
1).2.5億個整數中找出不重復的整數的個數,內存空間不足以容納這2.5億個整數。
有點像鴿巢原理,整數個數為2^32,也就是,我們可以將這2^32個數,劃分為2^8個區域(比如用單個文件代表一個區域),然後將數據分離到不同的區域,然後不同的區域在利用bitmap就可以直接解決了。也就是說只要有足夠的磁碟空間,就可以很方便的解決。
2).5億個int找它們的中位數。
這個例子比上面那個更明顯。首先我們將int劃分為2^16個區域,然後讀取數據統計落到各個區域里的數的個數,之後我們根據統計結果就可以判斷中位數落到那個區域,同時知道這個區域中的第幾大數剛好是中位數。然後第二次掃描我們只統計落在這個區域中的那些數就可以了。
實際上,如果不是int是int64,我們可以經過3次這樣的劃分即可降低到可以接受的程度。即可以先將int64分成2^24個區域,然後確定區域的第幾大數,在將該區域分成2^20個子區域,然後確定是子區域的第幾大數,然後子區域里的數的個數只有2^20,就可以直接利用direct addr table進行統計了。
6.資料庫索引
適用范圍:大數據量的增刪改查
基本原理及要點:利用數據的設計實現方法,對海量數據的增刪改查進行處理。
擴展:
問題實例:
7.倒排索引(Inverted index)
適用范圍:搜索引擎,關鍵字查詢
基本原理及要點:為何叫倒排索引?一種索引方法,被用來存儲在全文搜索下某個單詞在一個文檔或者一組文檔中的存儲位置的映射。
以英文為例,下面是要被索引的文本:
T0 = "it is what it is"
T1 = "what is it"
T2 = "it is a banana"
我們就能得到下面的反向文件索引:
"a": {2}
"banana": {2}
"is": {0, 1, 2}
"it": {0, 1, 2}
"what": {0, 1}
檢索的條件"what", "is" 和 "it" 將對應集合的交集。
正向索引開發出來用來存儲每個文檔的單詞的列表。正向索引的查詢往往滿足每個文檔有序頻繁的全文查詢和每個單詞在校驗文檔中的驗證這樣的查詢。在正向索引中,文檔占據了中心的位置,每個文檔指向了一個它所包含的索引項的序列。也就是說文檔指向了它包含的那些單詞,而反向索引則是單詞指向了包含它的文檔,很容易看到這個反向的關系。
擴展:
問題實例:文檔檢索系統,查詢那些文件包含了某單詞,比如常見的學術論文的關鍵字搜索。
8.外排序
適用范圍:大數據的排序,去重
基本原理及要點:外排序的歸並方法,置換選擇 敗者樹原理,最優歸並樹
擴展:
問題實例:
1).有一個1G大小的一個文件,裡面每一行是一個詞,詞的大小不超過16個位元組,內存限制大小是1M。返回頻數最高的100個詞。
這個數據具有很明顯的特點,詞的大小為16個位元組,但是內存只有1m做hash有些不夠,所以可以用來排序。內存可以當輸入緩沖區使用。
9.trie樹
適用范圍:數據量大,重復多,但是數據種類小可以放入內存
基本原理及要點:實現方式,節點孩子的表示方式
擴展:壓縮實現。
問題實例:
1).有10個文件,每個文件1G, 每個文件的每一行都存放的是用戶的query,每個文件的query都可能重復。要你按照query的頻度排序 。
2).1000萬字元串,其中有些是相同的(重復),需要把重復的全部去掉,保留沒有重復的字元串。請問怎麼設計和實現?
3).尋找熱門查詢:查詢串的重復度比較高,雖然總數是1千萬,但如果除去重復後,不超過3百萬個,每個不超過255位元組。
10.分布式處理 maprece
適用范圍:數據量大,但是數據種類小可以放入內存
基本原理及要點:將數據交給不同的機器去處理,數據劃分,結果歸約。
擴展:
問題實例:
1).The canonical example application of MapRece is a process to count the appearances of
each different word in a set of documents:
void map(String name, String document):
// name: document name
// document: document contents
for each word w in document:
EmitIntermediate(w, 1);
void rece(String word, Iterator partialCounts):
// key: a word
// values: a list of aggregated partial counts
int result = 0;
for each v in partialCounts:
result += ParseInt(v);
Emit(result);
Here, each document is split in words, and each word is counted initially with a "1" value by
the Map function, using the word as the result key. The framework puts together all the pairs
with the same key and feeds them to the same call to Rece, thus this function just needs to
sum all of its input values to find the total appearances of that word.
2).海量數據分布在100台電腦中,想個辦法高效統計出這批數據的TOP10。
3).一共有N個機器,每個機器上有N個數。每個機器最多存O(N)個數並對它們操作。如何找到N^2個數的中數(median)?
經典問題分析
上千萬or億數據(有重復),統計其中出現次數最多的前N個數據,分兩種情況:可一次讀入內存,不可一次讀入。
可用思路:trie樹+堆,資料庫索引,劃分子集分別統計,hash,分布式計算,近似統計,外排序
所謂的是否能一次讀入內存,實際上應該指去除重復後的數據量。如果去重後數據可以放入內存,我們可以為數據建立字典,比如通過 map,hashmap,trie,然後直接進行統計即可。當然在更新每條數據的出現次數的時候,我們可以利用一個堆來維護出現次數最多的前N個數據,當然這樣導致維護次數增加,不如完全統計後在求前N大效率高。
如果數據無法放入內存。一方面我們可以考慮上面的字典方法能否被改進以適應這種情形,可以做的改變就是將字典存放到硬碟上,而不是內存,這可以參考資料庫的存儲方法。
當然還有更好的方法,就是可以採用分布式計算,基本上就是map-rece過程,首先可以根據數據值或者把數據hash(md5)後的值,將數據按照范圍劃分到不同的機子,最好可以讓數據劃分後可以一次讀入內存,這樣不同的機子負責處理各種的數值范圍,實際上就是map。得到結果後,各個機子只需拿出各自的出現次數最多的前N個數據,然後匯總,選出所有的數據中出現次數最多的前N個數據,這實際上就是rece過程。
實際上可能想直接將數據均分到不同的機子上進行處理,這樣是無法得到正確的解的。因為一個數據可能被均分到不同的機子上,而另一個則可能完全聚集到一個機子上,同時還可能存在具有相同數目的數據。比如我們要找出現次數最多的前100個,我們將1000萬的數據分布到10台機器上,找到每台出現次數最多的前 100個,歸並之後這樣不能保證找到真正的第100個,因為比如出現次數最多的第100個可能有1萬個,但是它被分到了10台機子,這樣在每台上只有1千個,假設這些機子排名在1000個之前的那些都是單獨分布在一台機子上的,比如有1001個,這樣本來具有1萬個的這個就會被淘汰,即使我們讓每台機子選出出現次數最多的1000個再歸並,仍然會出錯,因為可能存在大量個數為1001個的發生聚集。因此不能將數據隨便均分到不同機子上,而是要根據hash 後的值將它們映射到不同的機子上處理,讓不同的機器處理一個數值范圍。
而外排序的方法會消耗大量的IO,效率不會很高。而上面的分布式方法,也可以用於單機版本,也就是將總的數據根據值的范圍,劃分成多個不同的子文件,然後逐個處理。處理完畢之後再對這些單詞的及其出現頻率進行一個歸並。實際上就可以利用一個外排序的歸並過程。
另外還可以考慮近似計算,也就是我們可以通過結合自然語言屬性,只將那些真正實際中出現最多的那些詞作為一個字典,使得這個規模可以放入內存。
8. 列哪些演算法可以應用於大數據挖掘
數據挖掘演算法都是可以用於大數據挖掘,大數據簡單來說只是說明數據量很大,一般指TB級別以上的,一台伺服器無法處理,需要分布式系統來處理。
其中,數據挖掘經典十大演算法為:C4.5,K-Means,SVM,Apriori,EM,PageRank,AdaBoost,KNN,NB和CART。
常見的分布式計算有Hadoop Spark等,如果要實時計算的,一般用Storm什麼的。
9. 大數據自上而下提升統計和演算法的效率
大數據自上而下提升統計和演算法的效率
我們在去開發這些計算體系時,不管是軟體、計算,其實都是在談大數據分析的概念性,什麼時候出現問題,我們如何達到高准確度,這只是這個問題的開始。其實作為一個計算科學家,我們經常會遇到很多的問題,有些是統計學方面的問題,但是我們沒有聯合統計學家一起考慮和解決這些問題。
比如說這個結果的一致性,那麼還有引導程序的理論,那麼就像常規的引導程序一樣,都會達到一些限值,從上至下的計算,統計學的利弊權衡,什麼意思呢?我們對數據計算的理解,也就是說更多的數據需要更多的計算,更多的計算能力。我們如何來做?到底是並行處理?還是子樣抽取等等。你給我更多的數據,我會更高興,因為我能夠獲得更高的准確度,我的錯誤會更小,我會以更低的成本獲得更正確的答案。對於統計學家來說這是好的,但是對於做計算的來說這個不大好,因為我們將這樣思考這個問題。也就是說給我一些數據,那麼我們有一個新的觀念,叫做控制的演算法弱化,比如說我的數據量不夠,我可以快速的處理它。數據太多,我的處理速度會慢下來。從計算角度來說,控制的演算法能夠讓我更快速的處理數據,也就是演算法的弱化。統計學的角度來說,能夠處理更多的數據,獲得更好的統計學上的答案性能提高。盡管計算的預算成本不變,但是我們能夠處理更多的數據,以更快的速度,我們付出的代價就是演算法的弱化。
那麼,這個坐標你們不經常看,橫軸指我們取樣的數量,縱軸代表的是運行時間。我們看一下到底有多少的錯誤。我們現在就要思考固定風險。比如說在我們錯誤率是0.01,這個座標的區域,對於統計學家來說,如果要固定風險的話,那麼必須有一定數量的樣品,才能夠獲得這樣的結果。所以,這是一個叫做典型的預計理論,大家都非常了解。同樣對於在計算機科學方面,我們有所謂的負載均衡的概念,不管你有多少個樣本,但是你一定要有足夠的運營時間,否則的話,你是無法解決這個問題的,這是非常明確的一點。
所以,我們看一下實際的演算法。有一定的運行時間,有固定的風險,在右邊使用的所有演算法,把演算法弱化,我們就可以處理更多的數據。下面我來談一下,這就是我們所說的問題降噪,所謂降噪就是在數據方面有一些屬於製造噪音的數據。我們如何做降噪?首先,我們假設可能的答案是X這樣的一個分樣,然後用高准確度覆蓋它,所以這是一個推理預估的過程。比如說我要找到X的值,它和Y是非常相似的,這是一個自然的預估。現在X是一個非常復雜的值,我無法做,所以我要做一個凸形的值域,我要做定性,同時可以獲得最優點,我需要把它放在一個可行的規模大小之內,那麼也就是任何一個固定風險都是基於X的。左邊是風險,我需要它的一半,這里存在復雜性,如果想知道更多的復雜性,你們可以看一些所謂理論處理方面的文獻,你們可以讀一下,來做這樣均衡的曲線。
我們看一下相關的內容,如果你要達到一定的風險,你必須要有一定的取樣點。這是一個C,也許這個C也是計算方面很難算出來的,所以我們需要做C子集的,把這個子集進行弱化,這樣我們就可以更好的計算了。我們可以做分層的層級,我們稱為池域,並且根據計算的復雜度進行排序的。同時,還有統計學的復雜性,然後進行一個權衡。你們可以從數學計算出這個曲線。在這里舉個例子,比如說X,剛才已經有人介紹過子集是什麼意思,然後你們可以定運行時間,還有取樣的復雜性,然後可以算出答案。你們看一下簡單的C,復雜的C,然後你們看一下運行的時間是在下降,復雜性是一個恆值,這樣你的演算法更簡單,可以用於大數據,既不會不會增加風險,也可以在舉證方面更加簡化。如果是一個信號的圖值,你的運行時間由PQ值決定,你們還有一個域值的話,我們會有一個恆定的取樣,大家可以同時按照「列」計算,獲得我們預期的准確度,而運行時間不變,大家可以自己看這些公式。
那麼,這種分析我希望大家能夠記住的是和這種理論計算科學,重點就是能夠把准確度放到一個水平。因為我們要去關心有關質量方面、統計學方面的風險,計算科學方面的演算法能夠幫助我們解決比較大的問題,就是大數據帶來的大問題。同時,我們還有很多的數據理論可以適用,我們不要從統計學簡單的角度來考慮,而是從計算的角度考慮。
也許你們還要去學一些統計學方面的基本理論,當然如果你們是學統計學的話,你們也要參加計算機科學的課程。對於兩門都學的人,你們應該把這兩個學科放到一起思考,不是統計學家只考慮統計學,計算機科學家只考慮計算機方面,我們需要解決統計學方面的風險。因此,我們可以更好的處理十萬個采樣點,都不會遇到問題。
10. 大數據時代 無處不在的演算法應用
大數據時代 無處不在的演算法應用
能不能講講演算法在工作中的運用?你個人學習演算法的過程是怎樣的?我對演算法還是有點怕。除此之外,你認為大學是應該多花時間學應用技術還是理論知識呢?
今天就來聊聊我自己學習演算法的過程,以及演算法在實際工作中的應用。
以前,我們認為大數據總是優於好演算法。也就是說,只要數據量足夠大,即使演算法沒有那麼好,也會產生好的結果。
前一陣子「極客時間」 App 發布了一條極客新聞:「演算法比數據更重要,AlphaGo Zero 完勝舊版。」新聞的內容是谷歌人工智慧團隊 DeepMind 發布了新版的 AlphaGo 計算機程序,名為 AlphaGo Zero。這款軟體能夠從空白狀態開始,不需要人類輸入任何命令,便可以迅速自學圍棋,並以 100 比 0 的戰績擊敗了上一代 AlphaGo。
AlphaGo Zero 最大的突破在於實現了「白板理論」。白板理論認為:嬰兒是一塊白板,可以通過後天學習和訓練來提高智力。AI 的先驅圖靈認為,只要能用機器製造一個類似於小孩的 AI,然後加以訓練,就能得到一個近似成人智力,甚至超越人類智力的 AI。
自學成才的 AlphaGo Zero 正是實現了這一理論。AlphaGo 的首席研究員大衛·席爾瓦(David Silver)認為,從 AlphaGo Zero 中可以發現,演算法比所謂的計算或數據量更為重要。事實上,AlphaGo Zero 使用的計算要比過去的版本少一個數量級,但是因為使用了更多原理和演算法,它的性能反而更加強大。
由此可見,在大數據時代,演算法的重要性日漸明晰。一個合格的程序員,必須掌握演算法。
我不知道大家是怎樣一步步開始精通演算法和數據結構的。大二時,我第一次接觸到了《數據結構》,因為從來沒有過這方面的思維訓練,當時的我學習這門課比較費力。那時候接觸到的編程比較少,所以並沒有很多實際經驗讓我欣賞和體味:一個好的數據結構和演算法設計到底 「美」 在哪裡。
開始學習的時候,我甚至有點死記硬背的感覺,我並不知道 「如果不這樣設計」,實際上會出現哪些問題。各種時間和空間復雜度對我而言,也僅僅是一些不能融入到實際問題的數學游戲。至於「每種最壞情況、平均情況的時間空間復雜度與各種排序」,這些內容為什麼那麼重要,當時我想,可能因為考試會考吧。
沒想到後來的時日,我又與演算法重新結緣。可能是因為萊斯大學給的獎學金太高了,所以每個研究生需要無償當五個學期的助教 。好巧不巧,我又被演算法老師兩次挑中當助教。所以,在命運強制下,一本《演算法導論》就這樣被我前前後後仔細學習了不下四遍。這樣的結果是,我基本做過整本書的習題,有些還不止做了一遍。我學習演算法的過程,就是反復閱讀《演算法導論》的過程。
那麼,學習演算法到底有什麼用處呢?
首先,演算法是面試的敲門磚國內的情況我不太清楚,但就矽谷的 IT 公司而言,不但電話面試偏演算法,現場面試至少有兩輪都是考演算法和編程的。
大一些老一些的公司,像谷歌、Facebook、領英、Dropbox 等,都是直接在白板上寫程序。小一些新一些的公司,如 Square、Airbnb 等,都是需要現場上機寫出可運行的程序。Twitter、Uber 等公司則是白板上機兼備,視情況而定。
雖說還有其它考系統設計等部分,但如果演算法沒有打好基礎,第一關就很難過,而且演算法要熟悉到能夠現場短時間內寫出正解,所以很多人准備面試前都需要刷題。
有一次我當面試官,電話面試另外一個人,當時是用 Codepad 共享的方式,讓對方寫一個可運行的正則表達式解析器。45 分鍾過去了,對方並沒有寫出來。我就例行公事地問:「你還有什麼問題想問或者想了解么?」 對方估計因為寫不出程序很有挫敗感,就反問:「你們平時工作難道就是天天寫正則表達式的解析器么?」
一瞬間,我竟無言以對。想了想,我回復說:「不用天天寫。那我再給你 15 分鍾,你證明給我看你還會什麼,或者有什麼理由讓我給你進一步面試的機會?」 對方想了一會,默默掛掉了電話。
老實說,我對目前面試中偏重演算法的程度是持保留意見的。演算法題答得好,並不能說明你有多牛。牛人也有因為不願刷題而馬失前蹄的時候。但是除了演算法測試,顯然也沒有更好的方法佐證候選人的實力;然而怎樣才能最優化面試流程,這也是個討論起來沒完的話題,並且每次討論必定無果而終。
其次,編程時用到的更多是演算法思想,而不是寫具體的演算法說到實際工作中真正需要使用演算法的機會,讓我想一想 —— 這個范圍應該在 10% 的附近遊走。
有些朋友在工作中遇到演算法場景多些,有的少些。更多的時候,是對業務邏輯的理解,對程序語言各種特性的熟練使用,對代碼風格和模式的把握,各種同步非同步的處理,包括代碼測試、系統部署是否正規化等等。需要設計甚至實現一個演算法的機會確實很少,即使用到,現學可能都來得及。
但是熟悉基本演算法的好處在於:如果工作需要讀的一段代碼中包含一些基本演算法思想,你會比不懂演算法的人理解代碼含義更快。讀到一段爛代碼,你知道為什麼爛,爛在哪,怎麼去優化。
當真的需要在程序中設計演算法的時候,熟悉演算法的你會給出一個更為完備的方案,對程序中出現的演算法或比較復雜的時間復雜度問題你會更有敏感性。熟悉演算法你還可以成為一個更優秀的面試官,可以和別的工程師聊天時候不被鄙視。
最後,不精通演算法的工程師永遠不是好工程師當然,除了演算法導論中那些已成為經典的基本演算法以及演算法思想(Divide-and-conquer,Dynamic programming)等,其實我們每天接觸到的各種技術中,演算法無處不在。
就拿人人都會接觸的存儲為例吧,各種不同的資料庫或者鍵值存儲的實現,就會涉及各種分片(Sharding)演算法、緩存失敗(Cache Invalidation)演算法、 鎖定(Locking)演算法,包括各種容錯演算法(多復制的同步演算法)。 雖然說平時不太會去寫這些演算法 —— 除非你恰恰是做資料庫實現的 —— 但是真正做到了解這項技術的演算法細節和實現細節,無論對於技術選型還是對自己程序的整體性能評估都是至關重要的。
舉個例子,當你在系統里需要一個鍵值存儲方案的時候,面對可供選擇的各種備選方案,到底應該選擇哪一種呢?
永遠沒有一種方案在所有方面都是最佳的。就拿 Facebook 開源的 RocksDB 來說吧。了解它歷史的人都知道,RocksDB 是構建在 LevelDB 之上的,可以在多 CPU 伺服器上高效運行的一種鍵值存儲。而 LevelDB 又是基於谷歌的 BigTable 資料庫系統概念設計的。
早在 2004 年,谷歌開始開發 BigTable,其代碼大量的依賴谷歌內部的代碼庫,雖然 BigTable 很牛,卻因此無法開源。2011 年,谷歌的傑夫·迪恩和桑傑·格瑪沃爾特開始基於 BigTable 的思想,重新開發一個開源的類似系統,並保證做到不用任何谷歌的代碼庫,於是就有了 LevelDB。這樣一個鍵值存儲的實現也用在了谷歌瀏覽器的 IndexedDB 中,對於谷歌瀏覽器的開源也提供了一定的支持。
我曾經在文章中提到過 CockroachDB,其實又可以看作是基於 RocksDB 之上的一個分布式實現。從另一個層面上講,CockroachDB 又可以說是 Spanner 的一個開源實現。知道這些,就知道這些資料庫或鍵值存儲其實都同出一系。再來看看 LevelDB 底層的 SSTable 演算法,就知道他們都是針對高吞吐量(high throughput),順序讀 / 寫工作負載(sequential read/write workloads)有效的存儲系統。
當然,一個系統里除了最基本的演算法,很多的實現細節和系統架構都會對性能及應用有很大的影響。然而,對演算法本身的理解和把握,永遠是深入了解系統不可或缺的一環。
類似的例子還有很多,比如日誌分析、打車軟體的調度演算法。
拿我比較熟悉的支付領域來說吧,比如信用卡 BIN 參數的壓縮,從服務端到移動 App 的數據傳輸,為了讓傳輸數據足夠小,需要對數據進行壓縮編碼。
每個國家,比如中國、韓國、墨西哥信用卡前綴格式都不一樣,如何盡量壓縮同時又不會太復雜,以至於影響移動 App 端的代碼復雜度,甚至形成 Bug 等,也需要對各種相關演算法有詳盡地了解,才有可能做出最優的方案。
關於演算法我們來總結一下:
在大數據時代,數據和演算法都同等重要,甚至演算法比計算能力或數據量更為重要。
如何學習演算法呢?讀經典著作、做題,然後在實踐中閱讀和使用演算法。
演算法是面試的敲門磚,可以幫助你得到一份自己喜歡的工作。
寫程序中用到的更多是演算法思想,不是寫具體的演算法。
不精通演算法的工程師永遠不會是一個優秀的工程師,只有對各種相關演算法有詳盡理解,才有可能做出最優的方案。