格雷碼那點事——遞歸非遞歸實現

簡介算法

  在一組數的編碼中,若任意兩個相鄰的代碼只有一位二進制數不一樣,則稱這種編碼爲格雷碼(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
 
  若是按照這個規則來生成格雷碼,是沒有問題的,可是這樣作太複雜了。若是仔細觀察格雷碼的結構,咱們會有如下發現:
  一、除了最高位(左邊第一位),格雷碼的位元徹底上下對稱(看下面列表)。好比第一個格雷碼與最後一個格雷碼對稱(除了第一位),第二個格雷碼與倒數第二個對稱,以此類推。
  二、 最小的重複單元是 0 , 1
0 00
0 01
0 11
0 10
1 10
1 11
1 01
1 00
  因此,在實現的時候,咱們徹底能夠利用遞歸,在每一層前面加上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位元格雷碼產生的。

 

算法實現spa

一、遞歸實現code

/**
     * 遞歸生成二進制格雷碼
     * 思路:一、得到n-1位生成格雷碼的數組
     *      二、因爲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;
    
    }

 

二、非遞歸實現blog

  /**
     * 非遞歸生成二進制格雷碼
     * 思路:一、得到n-1位生成格雷碼的數組
     *      二、因爲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;
    }

 

三、測試遞歸

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]);
    }
    }

 

四、結果:字符串

————————————————————遞歸實現————————————————
0000
0001
0011
0010
0110
0111
0101
0100
1100
1101
1111
1110
1010
1011
1001
1000
——————————————————非遞歸實現————————————————
0000
0001
0011
0010
0110
0111
0101
0100
1100
1101
1111
1110
1010
1011
1001
1000

  致謝:感謝您的耐心閱讀!it

相關文章
相關標籤/搜索