標籤:
簡介
在一組數的編碼中,若任意兩個相鄰的代碼只有一位位元不同,則稱這種編碼為格雷碼(Gray Code),另外由於最大數與最小數之間也僅一位元不同,即“首尾相連”,因此又稱迴圈碼或反射碼。在數字系統中,常要求代碼按一定順序變化。例如,按自然數遞增計數,若採用8421碼,則數0111變到1000時四位均要變化,而在實際電路中,4位的變化不可能絕對同時發生,則計數中可能出現短暫的其它代碼(1100、1111等)。在特定情況下可能導致電路狀態錯誤或輸入錯誤。使用格雷碼可以避免這種錯誤。格雷碼有多種編碼形式。
格雷碼(Gray Code)曾用過Grey Code、葛萊碼、格萊碼、戈萊碼、迴圈碼、反射二進位碼、最小差錯碼等名字,它們有的不對,有的易與其它名稱混淆,建議不要再使用這些曾用名。
格雷碼是一種具有反射特性和迴圈特性的單步自補碼,其迴圈和單步特性消除了隨機取數時出現重大錯誤的可能,其反射和自補特性使得對其進行求反操作也非常方便,所以,格雷碼屬於一種可靠性編碼,是一種錯誤最小化的編碼方式,因此格雷碼在通訊和測量技術中得到廣泛應用。
產生格雷碼
格雷碼(Gray Code)是一個數列集合,每個數使用二進位來表示,假設使用n位元來表示每個數字,任兩個數之間只有一個位元值不同。 例如以下為3位元的格雷碼: 000 001 011 010 110 111 101 100 。 如果要產生
n位元的格雷碼,那麼
格雷碼的個數為2^n. 假設原始的值從0開始,格雷碼產生的規律是:第一步,改變最右邊的位元值;第二步,改變右起第一個為1的位元的左邊位元;第三步,第四步重複第一步和第二步,直到所有的格雷碼產生完畢(換句話說,已經走了(2^n) - 1 步)。 用一個
例子來說明: 假設產生3位元的格雷碼,原始值位 000 第一步:改變最右邊的位元值: 001 第二步:改變右起第一個為1的位元的左邊位元: 011 第三步:改變最右邊的位元值: 010 第四步:改變右起第一個為1的位元的左邊位元: 110 第五步:改變最右邊的位元值: 111 第六步:改變右起第一個為1的位元的左邊位元: 101 第七步:改變最右邊的位元值: 100 如果按照這個規則來產生格雷碼,是沒有問題的,但是這樣做太複雜了。如果仔細觀察格雷碼的結構,我們會有以下發現: 1、除了最高位(左邊第一位),格雷碼的位元完全上下對稱(看下面列表)。比如第一個格雷碼與最後一個格雷碼對稱(除了第一位),第二個格雷碼與倒數第二個對稱,以此類推。 2、
最小的重複單元是 0 , 1。
000
001
011
010
110
111
101
100
所以,在實現的時候,我們完全可以利用遞迴,在每一層前面加上0或者1,然後就可以列出所有的格雷碼。 比如: 第一步:產生 0, 1 兩個字串。 第二步:在第一步的基礎上,每一個字串都加上0和1,但是每次只能加一個,所以得做兩次。這樣就變成了 00,01,11,10 (注意對稱)。 第三步:在第二步的基礎上,再給每個字串都加上0和1,同樣,每次只能加一個,這樣就變成了 000,001,011,010,110,111,101,100。 好了,這樣就把3位元格雷碼產生好了。 如果要產生4位元格雷碼,我們只需要在3位元格雷碼上再加一層0,1就可以了: 0000,0001,0011,0010,0110,0111,0101,0100,1100,1101,1110,1010,0111,1001,1000. 也就是說,
n位元格雷碼是基於n-1位元格雷碼產生的。
演算法實現
1、遞迴實現
/** * 遞迴產生二進位格雷碼 * 思路:1、獲得n-1位產生格雷碼的數組 * 2、由於n位產生的格雷碼位元是n-1的兩倍,故只要在n為格雷碼的前半部分加0,後半部分加1即可。 * @param n 格雷碼的位元 * @return 產生的格雷碼數組 */ public static String[] GrayCode(int n) { //數組的大小是2的n次方,因為n位的格雷碼有2的n次方種排列 String[] grayCodeArr = new String[(int)Math.pow(2, n)]; if(n < 1) { System.out.println("你輸入的格雷碼位元有誤!"); } if(1 == n) { grayCodeArr[0] = "0"; grayCodeArr[1] = "1"; return grayCodeArr; } //n-1 位格雷碼的產生方式 String[] before = GrayCode(n-1); for(int i = 0 ; i < before.length ; i++){ grayCodeArr[i] = "0" + before[i]; grayCodeArr[grayCodeArr.length -1 - i] = "1" + before[i]; } return grayCodeArr; }
2、非遞迴實現
/** * 非遞迴產生二進位格雷碼 * 思路:1、獲得n-1位產生格雷碼的數組 * 2、由於n位產生的格雷碼位元是n-1的兩倍,故只要在n為格雷碼的前半部分加0,後半部分加1即可。 * @param n 格雷碼的位元 * @return 產生的格雷碼數組 */ public static String[] GrayCode2(int n) { int num = (int)Math.pow(2, n);//根據輸入的整數,計算出此Gray序列大小 String[] s1 = {"0","1"};//第一個Gray序列 if(n < 1) { System.out.println("你輸入的格雷碼位元有誤!"); } for(int i=2;i<=n;i++){//迴圈根據第一個Gray序列,來一個一個的求 int p = (int)Math.pow(2, i);//到了第幾個的時候,來計算出此Gray序列大小 String[] si = new String[p]; for(int j=0;j<p;j++){//迴圈根據某個Gray序列,來一個一個的求此序列 if(j<(p/2)){ si[j] = "0" + s1[j];//原始序列前面加上"0" }else{ si[j] = "1" + s1[p-j-1];//原始序列反序,前面加上"1" } } s1 = si;//把求得的si,附給s1,以便求下一個Gray序列 } return s1; }
3、測試
public static void main(String[] args) { System.out.println("————————————————————遞迴實現————————————————"); String[] strArr = GrayCode(4); for(int i = 0 ; i < strArr.length ; i++) { System.out.println(strArr[i]); } System.out.println("——————————————————非遞迴實現————————————————"); String[] strArr2 = GrayCode2(4); for(int i = 0 ; i < strArr2.length ; i++) { System.out.println(strArr2[i]); } }
4、結果:
————————————————————遞迴實現————————————————0000000100110010011001110101010011001101111111101010101110011000——————————————————非遞迴實現————————————————0000000100110010011001110101010011001101111111101010101110011000
致謝:感謝您的耐心閱讀!
格雷碼那點事——遞迴非遞迴實現