信息表示方式的效率差異#

  • 對於同樣的信息,用不同編碼方式存儲,效率顯然不同
  • 例如表示一個人的年齡,用三個字符存儲需要三字節,但用一個八位無符號整數只需一字節,節省了 2/3 的空間
  • 優秀的從業者在做工程時,腦子裡一直會有一根弦,會認真考慮信息編碼的效率

二維矩陣的基本表示——方法零#

  • 二維矩陣最直接的做法是用一個二維數組表示,所需存儲空間是矩陣兩個維度的乘積(M × N)
  • 這種表示方法稱為「方法零」
  • 如果 M 和 N 非常大,就要占用大量空間

實際應用中的大矩陣#

  • 統計兩個單詞一前一後出現的頻率,英語大型字典有約 20 萬個單詞,矩陣有 400 億個元素,需要 80 GB 存儲
  • 計算 PageRank 時所需要的網頁之間的鏈接矩陣,假設有 100 億個網頁,矩陣就有 1 萬億億個元素

稀疏矩陣與三元組表示#

  • 現實世界裡,行和列兩個維度都很大的矩陣裡,非零元素占比極低
    • 在單詞同現矩陣中,非零元素的占比不到 1%
    • 在網頁鏈接的矩陣中,這個占比則小於一億分之一
  • 表示稀疏矩陣比較通行的方法是只記錄非零元素
  • 用一個三元組(行號、列號、元素值)的列表來表示矩陣

用三元組壓縮存儲空間的效果可以提高幾十倍、幾百倍甚至億萬倍。這才使得 Google 能夠實現 PageRank 算法。

三元組的進一步優化#

按行存儲的優化#

  • 原始三元組表中行號有很多重複,可以進一步精簡
  • 將三元組表拆成兩張表:
    • 表一:記錄每個非零元素的列號和數值
    • 表二:記錄每一行中非零元素在表一中的起始位置
  • 這種方式可以將存儲空間壓縮大約 1/3

按列建索引#

  • 矩陣乘法運算中需要按列來訪問非零元素,但三元組是按行存儲的
  • 解決方法:建立一個列索引表,記錄矩陣每一列中非零元素索引的起始位置
  • 同樣可以用類似的兩張表方式進行優化

矩陣的運算#

加法#

  • 兩個矩陣的加法本質是兩個有序線性表的合併
  • 用三元組存儲矩陣時,加法的時間複雜度取決於矩陣 X 和 Y 中的非零元素個數
  • 比直接用二維數組進行加法(需要遍歷所有元素,包括大量零元素)要高效得多

乘法#

  • 矩陣乘法中,結果矩陣 Z 的第 i 行、第 j 列的元素是矩陣 X 第 i 行和矩陣 Y 第 j 列相應元素相乘後求和
  • 使用三元組時元素是按行存儲的,需要能快速按列訪問非零元素,因此需要建立列索引

為什麼不複製兩份數據?#

不按照行和列分別存儲兩個備份,原因有二:

  1. 同步風險:在計算機系統中,同一個數據以不同形式存兩個備份,存在難以同步的風險,特別是多人協作的軟體
  2. 存儲成本:矩陣的元素可能是一個體量很大的記錄(例如基因數據),沒有必要將整個矩陣儲存兩遍

動態構建矩陣#

  • 在大部分應用中,矩陣中的數值都是用某種方法填入的
  • 例如建立一個詞語前後同現頻率的矩陣,需要用大量文本,過程中不斷發現新組合,需要將其插入已壓縮的矩陣中
  • 通常的解決方法是:在產生矩陣的過程中,採用哈希表來存儲矩陣中相應的數據項,等到整個矩陣構建完成再建立索引

圖與稀疏矩陣#

  • 在計算機科學中,圖(Graph)由頂點和邊構成,一張圖其實就對應一個二維矩陣
  • 現實世界裡絕大部分的圖都是稀疏的(邊的數量遠少於頂點數量的平方)
  • 例如互聯網網頁之間的超鏈接構成了一張極為稀疏的巨大圖
  • 自然語言處理中的二元組(Bigram)存儲也需要用到稀疏矩陣的方法

要點#

  • 利用計算機處理信息時,通常原始信息的數據量總是大於計算機的存儲容量,因此需要設計好信息的存儲方式,保證信息不丟失,同時存放進有限的存儲空間裡
  • 任何一個合格的計算機工程師,都應該掌握稀疏矩陣的存儲和使用方法,這是五級工程師的基本要求
  • 能夠實現一個大矩陣(比如需要成百上千台服務器進行存儲和運算)是對四級或 3.5 級工程師的要求