1. 程式人生 > >快取思想在演算法設計中的應用

快取思想在演算法設計中的應用

1.問題引入

我們先看一下簡單的斐波那契數列的遞迴演算法。百度百科中對該問題是這樣定義的:

斐波那契數列(Fibonacci sequence),又稱黃金分割數列、因數學家列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖為例子而引入,故又稱為“兔子數列”,指的是這樣一個數列:1、1、2、3、5、8、13、21、34、……在數學上,斐波納契數列以如下被以遞推的方法定義:F(1)=1,F(2)=1, F(3)=2,F(n)=F(n-1)+F(n-2)(n>=4,n∈N*)在現代物理、準晶體結構、化學等領域,斐波納契數列都有直接的應用,為此,美國數學會從1963年起出版了以《斐波納契數列季刊》為名的一份數學雜誌,用於專門刊載這方面的研究成果。

直接根據遞推關係式,我們可以設計常規的遞迴演算法:

fibonacci(n)
if n=1 return 1
if n=2 return 2
if n>2 return fibonacci(n-1)+fibonacci(n-2)

在這個方法中包含了大量重複的計算,比如在計算fibonacci(n)時需要計算一次fibonacci(n-2)的值,在計算fibonacci(n-1)時仍然要計算一次fibonacci(n-2)的值,問題的規模和計算的次數呈現如下的關係:

問題規模為n時子問題的計算次數
問題規模 計算次數
1和2 count(3)+count(4)
3 count(4)+count(5)
4 count(5)+count(6)
5 count(6)+count(7)
n-3 count(n-1)+count(n-2)=3
n-2 count(n-2)=2
n-1 count(n-1)=1

可以發現問題規模n的計算次數形成了一種“倒序”排列的斐波那契額數列,解決問題的所需要的計算次數和原問題呈現出相同的規模,執行效率會特別的慢。

事實上,我們可以使用一個數組快取下已經計算的子問題的值,當子問題被再次用到時,直接使用快取的值即可,避免重新計算,從而提升效率。

初始化陣列 A[1..n]={1,1,0,0,0...0}
fibonacci(n,A)
if n=1 return A[1]
if n=2 return A[2]
if n>2 
    A[n]=(A[n-1]=0?fabonacci(n-1):A[n-1])+(A[n-2]=0?fabonacci(n-2):A[n-2])
    return A[n]

 藉助陣列做資料快取中間結果值,可以大大提升演算法的執行效率。當然,對於斐波那契數列本身還有很多更好的優化方法,比如使用迭代進行一趟迴圈,或者藉助斐波那契的矩陣乘法定義進行優化等。我們這裡只是用這個例子來說明快取在演算法優化中的應用。

2. 思想分析

現在我們把關注點回到快取思想上來。從對斐波那契數列的優化過程中可以看到,快取是典型的以空間換取時間的策略。在計算機的設計與程式設計過程中,快取是一種經常用到的思想。

  • 在池化技術中,比如資料庫連線池,執行緒池等,為了避免大物件重複建立造成的資源浪費,將連線物件快取進池裡重複使用以提升效率。
  • 在計算機的體系結構中,為了調節CPU和記憶體之間的速度差異,使用二級快取的方式,根據8020原則,對經常使用到資料做快取,從而提升系統的執行效率。
  • 在分散式系統的設計中,將資料快取到離使用者最近的位置以解決大資料場景下熱點資料的訪問效能問題。
  • 在資料庫的訪問上,為了避免對硬碟的重複查詢,使用Redis、Ehcache和Memcached等開源工具對熱點查詢資料快取到記憶體中。
  • 在動態規劃的演算法中,我們依然可以看到快取的策略,比如在求解最長公共子序列時,使用了一個二維陣列來對子問題的最優解做快取。

綜上比較,快取思想的應用可以分成兩個方面:一是,在速度相差比較大的軟體/硬體之間,用以協調二者的速度差異,提升整體的吞吐率,比如計算機系統中的高速緩衝儲存器;二是,對計算資源消耗較大的中間結果做快取,以避免重複計算,比如資料庫連線池中對連線物件的快取避免重複對物件的建立,以及上述我們在優化斐波那契數列時對子問題中間結果的快取等。