一個高效的二進制數據補丁算法

     補丁算法在很多地方都很有用,可以用來製作發佈軟件的升級包、不同版本源代碼的增量備份、
數據的增量儲存等等;這裏介紹一種原創的高效的二進制數據補丁算法。
 對於文本文件,按行來處理可能是一種直觀的方案:求出新數據和老數據相比增加的行、刪除的行、修改的行等等;但這種算法對於一般的二進制數據不太適用,本文給出的是一個以二進制數據爲對象的解決辦法(當然它也能夠很好的處理文本文件);

 補丁算法簡 單來說就是求出兩個數據塊不一致的地方;這些不一致描述起來可能包括(有重疊):保留的數據、增加的數據、刪除的數據、修改的數據、移動的數據等。本算法 只使用保留的數據和增加的數據來表示這種不一致(注意,這種表示是完備的!)。 設原來的數據塊爲OldData,新的數據塊爲NewData,兩個數據之間應該有很多相同的數據段(否則就直接拷貝,不用打補丁了);現在要求求出一個 數據差PatchData,它能夠在只有OldData和PatchData的情況下重新生成NewData;

 算法的前提和假設:假 設兩個數據塊爲OldData大小M字節,NewData大小N字節;OldData和NewData有很多相同的數據段;如果我們構造一個 N(寬)*M(高) 大小的只包含false和true的布爾值矩陣E,令矩陣中i,j處的的元素值E[i,j],E[i,j]=(NewData[i]==OldData [j]); (注意:矩陣E不一定要實際生成,因爲它的元素值很容易得到); (這時E的(0,0)點在左上角,(N-1,M-1)點在右下角;)
    矩陣E中相鄰的連續的true值將組成很多的直線段;對於這些直線段推理可以得知: 橫線“-”是沒有任何意義的(說明OldData中有一段數據由單個字符組成),豎線“|”對於最後的壓縮效果意義不大(說明NewData中有一段數據 由單個字符組成),右斜線'/'說明有一段NewData和一段OldData數據正好順序顛倒(一般的應用中假設它很少見而不考慮);那麼算法現在的任 務就是要在矩陣E中找到多條由true組成的連續左斜線'/',並且這些左斜線長度大於一個常數(我得到的經驗值爲7,更短的直線段意義不大);在這些左 斜線中找出對“覆蓋”NewData最佳的一種直線組合;那麼補丁數據PatchData將由這些直線數據和NewData中沒有被覆蓋到的數據所組成; 理論上這將得到最好的補丁數據!

找出滿足要求的左斜線的算法:建立OldData的後綴數組OldDataStr,然後用NewData來匹配,從而找出所有的匹配(即左斜線); (嘗試過使用後綴樹,算法複雜度線性O(N),速度會快一點,但內存消耗太大了;其實N*log2(N)的算法也不慢:)
算法僞代碼:
  int NodeSize=1;//增大NodeSize的值可以減小內存需求,加快速度,但得到的PatchData會稍大一點
    OldDataStr=new (*Byte)[M/NodeSize];
    for (i=0 to (OldDataSize/NodeSize)-1) OldDataStr[i]=&OldData[i*NodeSize];
    QuickSortAsString(OldDataStr);//排序後綴數組;排序方法是把元素OldDataStr[i]看作是從 OldDataStr[i]開始到&OldData[M]結束的數據字符串(注意可以有0),按字符串大小排序;(這裏是最耗時的操作,可以做一 些排序優化)
   
    it=&NewData[0];//將it看作從it開始到&NewData[N]結束的數據字符串;
    while (it<&NewData[N])
    {
        itfinded=FindInOldDataStr(it);//在OldDataStr表中查找it字符串的插入位置;C++中可以利用LowerBound
        itfinded=BestEqualLength(itfinded)//在itfinded字典位置附近得到最接近it的位置;也就是最長的相同字符串前綴(其他長度的捨棄)
        if (EqualLength(itfinded,it)>AConstLength)// AConstLength=7,它體現分段代價
           AddOldLine(it,itfinded)//找到了NewData中的一段數據可以用OldData中的數據代替
        ++it;
    }
處理左斜線:令 找到的所有左斜線爲集合L,現在的任務是找到一條穿過這些直線的最佳路徑;(先對L進行排序)如果其中一條線a完全覆蓋另一條線b,則從L中刪除b;如果 a,b兩個線段在一條直線上,並且間隔很小,則他們是可以優化的(優化方法是:把它們聯接成一條直線段,中間的間隔部分對應的NewData數據同樣需要 放到PatchData中),對於不容易優化的獨立直線,並且比較短(經驗值18),可以從L中刪除;對於沒有重疊的線段,直接選爲路徑直線段,對於有重 疊的線段,需要計算通過它們的各種組合所產生的大小代價(帶權值的路徑搜索);簡單處理的話也可以一次只處理少量重疊的線段,畢竟重疊出現的不多;
 
生成補丁數據PatchData:將路徑直線段數據保存到PacthData中,將沒有被直線段覆蓋的NewData數據拷貝到PatchData中;

考慮中的改進:爲exe文件作優化,利用exe文件的特點(就是大量偏移地址),先做一些處理,然後再使用上面的算法。
 
數據還原算法:  輸入PatchData和OldData,輸出NewData。 按PatchData的直線段描述從OldData中拷貝數據到新的NewData數據區中,然後拷貝PachData中保存的NewData數據到現在的NewData數據區中,完成。
(  我使用過VPatch,它是一個開放源代碼的補丁工具,二進制壓縮,很好用,在inno安裝工具中也有使用,壓縮效果還不錯;地址:http://www.tibed.net/vpatch/
    我的算法產生的補丁數據(一些遊戲程序)和VPatch產生的補丁數據在壓縮後比較,壓縮率比VPatch高40%左右! 速度也快出很多!



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=658863

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章