幾個重要的算法概念

反向索引


一個散列表,將單詞映射到包含它的頁面。這種數據結構被稱爲 反向索引(inverted index),常用於創建搜索引擎。
這裏非常簡單地說說搜索引擎的工作原理。假設你有三個網頁,內容如下。

在這裏插入圖片描述

我們根據這些內容創建一個散列表。 這個散列表的**鍵爲單詞**,**值** 爲包含指定單詞的**頁面**。現在假設有用戶搜索 `hi`,在這種情況下,搜索引擎需要檢查哪些頁面包含 `hi`。
單詞 頁面
HI A, B
THERE A, C
ADIT B
WE C
GO C

傅里葉變換


傅里葉變換有一個絕佳的比喻:給它一杯冰沙,它能告訴你其中包含哪些成分。換言之,給定一首歌曲,傅里葉變換能夠將其中的各種頻率分離出來.
這種理念雖然簡單,應用卻極其廣泛。例如,如果能夠將歌曲分解爲不同的頻率,就可強化你關心的部分,如強化低音並隱藏高音。傅里葉變換非常適合用於處理信號,可使用它來壓縮音樂。爲此,首先需要將音頻文件分解爲音符。傅里葉變換能夠準確地指出各個音符對整個歌曲的貢獻,讓你能夠將不重要的音符刪除。這就是MP3格式的工作原理!傅里葉變換還被用來地震預測和DNA分析。


並行算法


我們身處一個處理器速度越來越快的時代,如果你要提高算法的速度,可等上幾個月,屆時計算機本身的速度就會更快。但這個時代已接近尾聲,因此筆記本電腦和臺式機轉而採用多核處理器。爲提高算法的速度,你需要讓它們能夠在多個內核中並行地執行!
並行算法設計起來很難,要確保它們能夠正確地工作並實現期望的速度提升也很難。有一點是確定的,那就是速度的提升並非線性的,因此即便你的筆記本電腦裝備了兩個而不是一個內核,算法的速度也不可能提高一倍,其中的原因有兩個。

  • 並行性管理開銷。假設你要對一個包含 10001000 個元素的數組進行排序,如何在兩個內核之間分配這項任務呢?如果讓每個內核對其中 500500 個元素進行排序,再將兩個排好序的數組合併成一個有序數組,那麼合併也是需要時間的。
  • 負載均衡。假設你需要完成 1010 個任務,因此你給每個內核都分配 55 個任務。但分配給內核 A 的任務都很容易, 1010 秒鐘就完成了,而分配給內核 B 的任務都很難, 11分鐘才完成。這意味着有那麼 5050 秒,內核 B 在忙死忙活,而內核 A 卻閒得很!你如何均勻地分配工作,讓兩個內核都一樣忙呢?
    要改善性能和可擴展性,並行算法可能是不錯的選擇!

MapReduce


在並行算法只需兩到四個內核時,完全可以在筆記本電腦上運行它,但如果需要數百個內核呢?在這種情況下,可讓算法在多臺計算機上運行。 MapReduce 是一種流行的 分佈式算法,你可通過流行的開源工具Apache Hadoop來使用它。


分佈式算法爲何很有用


假設你有一個數據庫表,包含數十億乃至數萬億行,需要對其執行復雜的 SQL 查詢。在這種情況下,你不能使用 MySQL,因爲數據表的行數超過數十億後,它處理起來將很喫力。相反,你需要通過 Hadoop 來使用 MapReduce。又假設你需要處理一個很長的清單,其中包含 100100 萬個職位,而每個職位處理起來需要 1010 秒。如果使用一臺計算機來處理,將耗時數月!如果使用 100100 臺計算機來處理,可能幾天就能完工。

分佈式算法非常適合用於在短時間內完成海量工作,其中的 MapReduce 基於兩個簡單的理念:映射(map)函數歸併(reduce)函數


映射函數


映射函數很簡單,它接受一個數組,並對其中的每個元素執行同樣的處理。例如,下面的映射函數將數組的每個元素翻倍。

arr1 = [1, 2, 3, 4, 5]
arr2 = map(lambda x: 2 * x, arr1) # [2, 4, 6, 8, 10]

arr2 包含 [2, 4, 6, 8, 10]: 將數組 arr1 的每個元素都翻倍!將元素翻倍的速度非常快,但如果要執行的操作需要更長的時間呢?請看下面的僞代碼:

arr1 = # A list of URLs
arr2 = map(download_page, arr1)

在這個示例中,你有一個 URL 清單,需要下載每個 URL 指向的頁面並將這些內容存儲在數組 arr2 中。對於每個 URL,處理起來都可能需要幾秒鐘。如果總共有 10001000URL,可能耗時幾小時!
如果有 100100 臺計算機,而 map 能夠自動將工作分配給這些計算機去完成就好了。這樣就可同時下載 100100 個頁面,下載速度將快得多!這就是 MapReduce 中“映射” 基於的理念。


歸併函數


歸併函數可能令人迷惑,其理念是將很多項歸併爲一項。映射是將一個數組轉換爲另一個數組。

在這裏插入圖片描述
映射的作用

而歸併是將一個數組轉換爲一個元素。

在這裏插入圖片描述
歸併的作用

下面是一個示例:

arr1 = [1, 2, 3, 4, 5]
reduce(lambda x, y: x+y, arr1) # 15

在這個示例中,你將數組中的所有元素相加: 1+2+3+4+5=151 + 2 + 3 + 4 + 5 = 15
MapReduce 使用這兩個簡單概念在多臺計算機上執行數據查詢。數據集很大,包含數十億行時,使用 MapReduce 只需幾分鐘就可獲得查詢結果,而傳統數據庫可能要耗費數小時。


布隆過濾器和HyperLogLog


布隆過濾器HyperLogLog 都是概率型選擇算法
假設給定一個元素,你需要判斷它是否包含在這個集合中,例如, Google 可能有一個龐大的散列表,其中的 是已蒐集的網頁。
散列表的平均查找時間爲 O(1)O(1),只是 Googl e需要建立數萬億個網頁的索引,因此這個散列表非常大,需要佔用大量的存儲空間。此時,可以使用 布隆過濾器 ,它可以給出一個近似的結果,也許這個結果不正確,布隆過濾器的優點在於佔用的存儲空間很少。
HyperLogLog 是一種類似於布隆過濾器的算法
如果 Google 要計算用戶執行的不同搜索的數量,或者 Amazon 要計算當天用戶瀏覽的不同商品的數量,要回答這些問題,需要耗用大量的空間!對 Google 來說,必須有一個日誌,其中包含用戶執行的不同搜索。有用戶執行搜索時, Google 必須判斷該搜索是否包含在日誌中:如果答案是否定的,就必須將其加入到日誌中。即便只記錄一天的搜索,這種日誌也大得不得了!
HyperLogLog 近似地計算集合中不同的元素數,與布隆過濾器一樣,它不能給出準確的答案,但也八九不離十,而佔用的內存空間卻少得多。


SHA算法


散列表中,散列函數非常重要,我們通常希望 散列函數 的結果是均勻分佈的。散列函數 接受一個字符串,並返回一個索引號。
常用的 散列函數安全散列函數(secure hash algorithm, SHA),其給定一個字符串的輸入,生成另一個字符串的輸出(SHA 生成的散列值很長)。通常用於 比較文件生成密碼

在這裏插入圖片描述

假設你有一個$4$ GB的文件,並要檢查朋友是否也有這個大型文件。你們只需要對兩個文件的 SHA 散列值進行比較即可。

在這裏插入圖片描述

Google 保存的並非是用戶的密碼,而是由用戶密碼生成的 SHA 散列值,而且SHA並不支持由散列值逆推輸入字符。這樣即使 Gmail 遭到攻擊,攻擊者也無法獲取用戶的密碼。
SHA 是一系列的算法:SHA-0、SHA-1、SHA-2、SHA-3。目前,SHA-0、SHA-1已被發現存在一些問題。SHA-2、SHA-3會更加安全。但是,安全只是暫時的。


局部敏感的散列算法


SHA 具有局部不敏感的特徵,即兩個字符串存在微小的不同,也會產生兩個差別巨大的結果。例如:dogcd6357dog \rightarrow cd6357dote392dadot \rightarrow e392da
但是,有的時候我們希望散列函數具有 局部敏感 的特徵。例如兩個只有細微不同的輸入字符串產生兩個只有細微不同的散列值。Smibash 可以實現這樣的效果。

  1. Google 使用 Simhash 來判斷網頁是否已蒐集。
  2. 老師可以使用 Simhash 來判斷學生的論文是否是從網上抄的。
  3. Scribd 允許用戶上傳文檔或圖書,以便與人分享,但不希望用戶上傳有版權的內容!這個網站可使用 Simhash 來檢查上傳的內容是否與小說《哈利·波特》類似,如果類似,就自動拒絕。

Deffie-Hellman密鑰交換


Deffie-Hellman 解決了一個古老的加密問題:如何對信息加密,以便只有收件人看懂?
Deffie-Hellman 使用兩個密鑰:公鑰和私鑰。經過公鑰加密的信息只有通過對應的私鑰才能打開。公鑰是可以向外發佈的,而私鑰是自己保存的。Deffie-Hellman 算法解決了如下兩個問題:

  1. 雙方無需知道加密算法,他們不必會面協商要使用的加密算法。
  2. 要破解加密的消息比登天還難。

參考資料


  1. 《算法新解》,有興趣的同學可以看一下,講得還是很不錯得。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章