1. 程式人生 > >輕鬆理解CRC差錯檢測演算法七 -註解

輕鬆理解CRC差錯檢測演算法七 -註解

原文第9部分的補充

原因

不得不承認把這個系列的翻譯繼續下去是很有挑戰的。最近加班多,工作之餘的精力就比較少了。堅持下去的想法還是有的,但回顧漸忘的初心,做這件事情的意義是在於幫助那些想把CRC演算法學明白,而英文又差那麼一點的人們,幫助他們從最根本的原理上來理解這個算法系列。我在之前自己看的過程中就發現作者的思路時有跳躍性。我自己在看的時候思路經常出現斷續,作者說的標題“輕鬆理解”其實是有點誇大了。不符合我的習慣,我的習慣是每一個演進,都有它的原理在裡面,可以直接看到,不需要讀者再去思考。所以有了寫這篇註解動機。
為了保持譯文的完整和原本的條理,不方便直接在原文中加入大量的註解,另外只有證明我自己確實是理解了這部分,才能說明我翻譯的基礎是牢固的。所以才有了這一篇,作為原文第9節的註解。這裡我會把我理解這部分的思路一步步展現出來。

原文梳理

第9節作為第8節的進一步,遠遠沒有第8節那麼容易理解。第8節,在講解基本的直接實現的演算法,其原理也非常簡單,與長除法對應,也就是用移位與異或來實現長除法。第9節中,在第8節直接實現的基礎上,作者介紹了表驅動實現。他的思路是,介紹表驅動的2個基礎理論

1. 由poly和資訊的前8位,可以確定第8次迭代後,暫存器的最高位值是0或者1。
2. 一個特定數a多次在不同的偏移位置與同一數b相異或,得到的結果,與a經過相同的偏移的值相異或後,再與b相異或得到的結果相同。用公式表達就是:
[(a << 1) ^ b] ^ a ^ (a << 2) ^ (a << n) =
[(a << 1) ^ a ^ (a << 2) ^ (a << n)] ^ b
有了這樣的兩個條件後,然後實然一下,作者不厚道地把表驅動實現的虛擬碼擺了出來。這裡的跳躍性太大了,相信許多原文的人都沒有搞明白。我要做的就是把這部分空白補上。

填補空白

填補之前,我們還是把思路再前移動一個環節看下,為什麼長除法可以用移位+異或來實現。這樣我們的思路就前後銜接,完美無瑕。
這個圖中展示出的是長除法的計算過程。用虛擬碼來描述就是:
1. 把W位除數與被除數左對齊。
2. 如果被除數不小於除數,即被除數最高位是1,執行第3步。否則執行第4步。
3. 執行與除數的CRC減法(異或)運算。得到一個差值,這個差值長度一定比除數要短1,即W-1。跳到第5步。
4. 把除數全部位設0,執行CRC減法運算,自右向左保留W-1位。跳到第5步。
5. 如果被除數後面還有數位,則在差值尾部補上被除數的下一位,跳到第2步。否則運算結束,得到的最後差,即為餘數。
這裡寫圖片描述


這個演算法是如何變為直接實現演算法呢?
其實不難看出,在上述演算法中,第2步中的判斷就是關鍵。我們知道一個好的poly的首位肯定是1。所以要看是否執行異或操作,全看當前與poly對齊時,被除數的最高位是否是1。是1則相異或,0則不異或。

那麼為什麼poly比暫存器要寬1位呢?在長除法的例子中,可以看到與poly對齊時,除數最高位無論是0,還是1,在下次運算中,它都會被拋棄,因為如果1,它與poly相異或得到的是0,如果是0,則與全0異或時得到的還是0。也就是說,無論什麼情況下,它都會變成0。
這裡寫圖片描述
夏農-布朗資訊理論告訴我們,一個事件的資訊量與它發生的概率的對數值成反比,即越可能發生的事情其資訊量就越小,必然發生的事情,其資訊量就是0了。沒有資訊量,在資訊時代就沒有用處了。沒有用處,就不需要留下它了。
第9節中提到的兩個基礎理論,應該很容易理解。我關注的是如何把它們過渡到表驅動實現。
表驅動中,資訊的處理單元是位元組。直接實現中處理的資訊單元是位。
由於我們關心的是最後餘數,也就是資訊處理完成後,留在暫存器裡的值。中間過程中會發生什麼,中間暫存器的值會變成什麼,我們並不關心,只要它的值可以傳遞到最後,參與最後一次異或運算即可。如果沒有這個作為跳板,很難明白,為什麼可以把位單元操作,變換為位元組單元的操作。按照這個目的作為指引。我們看下,結構長除法的例子,看下在運算過程中到底發生了什麼。
例子中,除數的位寬為5。那麼來看下前5次運算。用R代表暫存器,p代表除數。
R ^ p ^ (p >> 1) ^ 0 ^ 0
得到的餘數是01011。沒錯這就是我們關心的,前5次運算後,暫存器中的值,正是它將直接參與到下個位元組中的運算中。
按照前面提到的第2個基礎,這5次運算可以看作是如下,方便後面引用寫作R ^ X:
R ^ (p ^ (p >> 1) ^ 0 ^ 0) = R ^ X
OK,這裡就可以看出為什麼這5次運算可以合併為1次 。首先R中的值只會有2^5種可能,也就是0到31。這個情況是可以提前知道的,也是必然的。由於p是提前也知道的,那麼與每一個R值對應的前5次的運算是確切可以知道的,也就是X的值是可以提前知道的。X的值就是我們要在表格裡索引的值。也就實現了一次處理5個位的資訊了。
前5個位處理完成後,X與其後資訊組成一個新的資訊列,重複取前5個來索引得到後面的新餘數,最終完成整個表驅動演算法。
看到這裡,相信每個人都已經明白了。:-)