格雷碼

原題

  The gray code is a binary numeral system where two successive values differ in only one bit.
  Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.
  For example, given n = 2, return [0,1,3,2]. Its gray code sequence is:java

00 - 0
01 - 1
11 - 3
10 - 2

 

  Note:
  For a given n, a gray code sequence is not uniquely defined.
  For example, [0,2,3,1] is also a valid gray code sequence according to the above definition.
  For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.算法

題目大意

  給定n,輸出長度爲n的格雷碼錶。app

解題思路

  遞歸生成碼錶
  這種方法基於格雷碼是反射碼的事實,利用遞歸的以下規則來構造:
  1位格雷碼有兩個碼字
  (n+1)位格雷碼中的前2n個碼字等於n位格雷碼的碼字,按順序書寫,加前綴0
  (n+1)位格雷碼中的後2n個碼字等於n位格雷碼的碼字,按逆序書寫,加前綴1編碼

代碼實現

算法實現類spa

import java.util.LinkedList;
import java.util.List;

public class Solution {

    public List<Integer> grayCode(int n) {
        List<Integer> result = new LinkedList<>();
        if (n >= 0) {
            // 格雷碼的前半部分
            result.add(0);
            // 格雷碼最高位的值(非0時)
            int t = 1;
            // 每一次外循環求出的是位數i+1位的格雷碼錶,其至關於長度爲i+1位的格雷碼錶的前半部分
            for (int i = 0; i < n; i++) {
                // 求出的長度爲i+1位格雷碼錶的後半部分,前半部分由長度爲i位的格雷碼錶給出
                for (int j = result.size() - 1; j >= 0; j--) {
                    result.add(result.get(j) ^ t);
                }
                // 最高位右移
                t <<= 1;
            }
        }
        return result;
    }
}

 

 

在一組數的編碼中,若任意兩個相鄰的代碼只有一位二進制數不一樣,則稱這種編碼爲格雷碼(Gray Code),另外因爲最大數與最小數之間也僅一位數不一樣,即「首尾相連」,所以又稱循環碼反射碼.net

 

那麼格雷碼有什麼規律呢?當用一個二進制表示格雷碼時,code

n=1時,格雷碼是:0,1;blog

       n=2時,格雷碼是:00,01,11,10;遞歸

       n=3時,格雷碼是:000,001,011,010,110,111,101,100;圖片

因而可知,格雷碼都有一個廣泛規律:

       第一:格雷碼的個數爲2的n次方。如n=3時,有8個格雷碼。

       第二:格雷碼的最高位有規律,前面一半爲0,後面一半爲1;

       第三:格雷碼除去最高位後,是對稱的。10對10,11對11.並且由n-1的格雷碼組成。

上面三點規律能夠從下圖(圖片來源於其它博客)看出:

    

由此可知能夠遞歸實現,代碼爲:

 

[java] view plain copy

 

  1. public class N_gray {  
  2.   
  3.     public static void main(String[] args){  
  4.         Scanner sc = new Scanner(System.in);  
  5.         String s = sc.nextLine();  
  6.         int i = Integer.parseInt(s);  
  7.         StringBuffer[] sb = n_gray(i);  
  8.           
  9.         for(int j=0;j<sb.length;j++){  
  10.             if(j>0){  
  11.                 System.out.print(",");  
  12.             }  
  13.             System.out.print(sb[j]);  
  14.         }  
  15.     }  
  16.       
  17.     public static StringBuffer[] n_gray(int n){  
  18.         int length = (int)Math.pow(2, n);   //根據n的值計算格雷碼的個數  
  19.         StringBuffer[] sbArray = new StringBuffer[length];  
  20.         if(n==1){  
  21.             sbArray[0] = new StringBuffer("0");  
  22.             sbArray[1] = new StringBuffer("1");  
  23.         }else{  
  24.             StringBuffer[] temp = n_gray(n-1);<span style="white-space:pre">  </span>//遞歸調用n-1時的格雷碼  
  25.             for(int i=0;i<length;i++){  
  26.                 if(i<length/2){  
  27.                     sbArray[i] = new StringBuffer(temp[i]);  
  28.                     sbArray[i].insert(0, "0");//StringBuffer[]中前半部分的格雷碼最高位插入0  
  29.                 }else{  
  30.                     sbArray[i] = new StringBuffer(temp[length-i-1]);  
  31.                     sbArray[i].insert(0, "1");//StringBuffer[]中後半部分的格雷碼最高位插入1  
  32.                 }  
  33.             }  
  34.               
  35.         }  
  36.           
  37.         return sbArray;  
  38.     }  
  39.  

問題:產生n位元的全部格雷碼。

 

格雷碼(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

 

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位元格雷碼產生的。

 

若是可以理解上面的部分,下面部分的代碼實現就很容易理解了。

[java] view plain copy

 

  1. public String[] GrayCode(int n) {  
  2.   
  3.     // produce 2^n grade codes  
  4.     String[] graycode = new String[(int) Math.pow(2, n)];  
  5.   
  6.     if (n == 1) {  
  7.         graycode[0] = "0";  
  8.         graycode[1] = "1";  
  9.         return graycode;  
  10.     }  
  11.   
  12.     String[] last = GrayCode(n - 1);  
  13.   
  14.     for (int i = 0; i < last.length; i++) {  
  15.         graycode[i] = "0" + last[i];  
  16.         graycode[graycode.length - 1 - i] = "1" + last[i];  
  17.     }  
  18.   
  19.     return graycode;  
  20. }  

格雷碼還有一種實現方式是根據這個公式來的 G(n) =  B(n) XOR B(n+1), 這也是格雷碼和二進制碼的轉換公式。代碼以下:

[java] view plain copy

 

  1. public void getGrayCode(int bitNum){  
  2.     for(int i = 0; i < (int)Math.pow(2, bitNum); i++){  
  3.         int grayCode = (i >> 1) ^ i;  
  4.         System.out.println(num2Binary(grayCode, bitNum));  
  5.     }  
  6. }  
  7. public String num2Binary(int num, int bitNum){  
  8.     String ret = "";  
  9.     for(int i = bitNum-1; i >= 0; i--){  
  10.         ret += (num >> i) & 1;  
  11.     }  
  12.     return ret;  
  13. }  

  什麼是格雷碼?

         下面一段來自百度百科:

          在一組數的編碼中,若任意兩個相鄰的代碼只有一位二進制數不一樣,則稱這種編碼爲格雷碼(Gray Code),另外因爲最大數與最小數之間也僅一位數不一樣,即「首尾相連」,所以又稱循環碼反射碼。在數字系統中,常要求代碼按必定順序變化。例如,按天然數遞增計數,若採用8421碼,則數0111變到1000時四位均要變化,而在實際電路中,4位的變化不可能絕對同時發生,則計數中可能出現短暫的其它代碼(1100、1111等)。在特定狀況下可能致使電路狀態錯誤或輸入錯誤。使用格雷碼能夠避免這種錯誤。格雷碼有多種編碼形式。

     因而可知,使用格雷碼可讓數字電路的變化趨於穩定。

 

特色

格雷碼屬於可靠性編碼,是一種錯誤最小化的編碼方式。
由於,雖然天然二進制碼能夠直接由數/模轉換器轉換成模擬信號,但在某些狀況,例如從十進制的3轉換爲4時二進制碼的每一位都要變,能使數字電路產生很大的尖峯電流脈衝。而格雷碼則沒有這一缺點,它在相鄰位間轉換時,只有一位產生變化。它大大地減小了由一個狀態到下一個狀態時邏輯的混淆。因爲這種編碼相鄰的兩個碼組之間只有一位不一樣,於是在用於方向的轉角位移量-數字量的轉換中,當方向的轉角位移量發生微小變化(而可能引發數字量發生變化時,格雷碼僅改變一位,這樣與其它編碼同時改變兩位或多位的狀況相比更爲可靠,便可減小出錯的可能性。
格雷碼是一種絕對編碼方式,典型格雷碼是一種具備反射特性和循環特性的單步自補碼,它的循環、單步特性消除了隨機取數時出現重大偏差的可能,它的反射、自補特性使得求反很是方便。
因爲格雷碼是一種變權碼,每一位碼沒有固定的大小,很難直接進行比較大小和算術運算,也不能直接轉換成液位信號,要通過一次碼變換,變成天然二進制碼,再由上位機讀取。
典型格雷碼是一種採用絕對編碼方式的準權碼,其權的絕對值爲2^i-1(設最低位i=1)。
格雷碼的十進制數奇偶性與其碼字中1的個數的奇偶性相同。

 

      那麼格雷碼有什麼規律呢?當用一個二進制表示格雷碼時,也就是n=1時,格雷碼只有0,1;

       n=2時,格雷碼是:00,01,11,10;

       n=3時,格雷碼是:000,001,011,010,110,111,101,100;

       因而可知,格雷碼都有一個廣泛規律:
       第一:格雷碼的個數爲2的n次方。如n=3時,有8個格雷碼。

       第二:格雷碼的最高位有規律,前面一半爲0,後面一半爲1;

       第三:格雷碼除去最高位後,是對稱的。10對10,11對11.並且由n-1的格雷碼組成。

      上面三點規律能夠從下圖看出:

     

        以上可知,只要用個遞歸就可實現。

        下面是實現代碼:

 

[java] view plain copy

 

  1. import java.io.*;  
  2. import java.util.Scanner;  
  3. public class N_gray {  
  4.   
  5.     public static void main(String[] args){  
  6.         //難道java中輸入一個整數要經歷不少東西嗎?  
  7.         Scanner sc=new Scanner(System.in);  
  8.         String n=sc.nextLine();  
  9.         int N=Integer.parseInt(n);  
  10.         StringBuffer[] gray_code=get_gray(N);  
  11.         for(int i=0;i<gray_code.length;i++){  
  12.             System.out.println(gray_code[i]);  
  13.         }  
  14.     }  
  15.       
  16.     public static StringBuffer[] get_gray(int n){  
  17.         int length=(int)Math.pow(2, n);  
  18.         StringBuffer[] gray_code=new StringBuffer[length];  
  19.         if(n==1){  
  20.             gray_code[0]=new StringBuffer("0");  
  21.             gray_code[1]=new StringBuffer("1");  
  22.               
  23.               
  24.         }  
  25.         else{  
  26.             StringBuffer[] temp=get_gray(n-1);  
  27.             for(int i=0;i<length;i++){  
  28. //若是用=,只是傳引用,因此對gray_code[i]插值的時候會影響temp,從而致使最後的結果錯誤。  
  29. //gray_code[i]=temp[i%(length/2)];  
  30.                 
  31.                 if(i<(length/2)){  
  32.                     gray_code[i]=new StringBuffer((temp[i]));  
  33.                     gray_code[i].insert(0, "0");  
  34.                 }  
  35.                 else{  
  36.                     gray_code[i]=new StringBuffer((temp[length-i-1]));  
  37.                     gray_code[i].insert(0, "1");  
  38.   
  39.                 }  
  40.             }  
  41.               
  42.               
  43.         }  
  44.         return gray_code;  
  45.     }  
  46. }  


格雷碼還有一種實現方式是根據這個公式來的G(n) =  B(n) XOR B(n+1), 這也是格雷碼和二進制碼的轉換公式。代碼以下:

 

 

[java] view plain copy

 

  1. public void getGrayCode(int bitNum){  
  2.     for(int i = 0; i < (int)Math.pow(2, bitNum); i++){  
  3.         int grayCode = (i >> 1) ^ i;  
  4.         System.out.println(num2Binary(grayCode, bitNum));  
  5.     }  
  6. }  
  7.    public String num2Binary(int num, int bitNum){  
  8.     String ret = "";  
  9.     for(int i = bitNum-1; i >= 0; i--){  
  10.         ret += (num >> i) & 1;  
  11.     }  
  12.     return ret;  
  13. }  


 

 

彙總:

 

[java] view plain copy

 

  1. //格雷碼生成  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.LinkedList;  
  5. import java.util.List;  
  6. import java.util.Scanner;  
  7.   
  8. public class GrayCode {  
  9.     private static Scanner cin;  
  10.   
  11.     public List<Integer> grayCode(int n) {  
  12.         List<Integer> result = new LinkedList<>();  
  13.         if (n >= 0) {  
  14.             // 格雷碼的前半部分  
  15.             result.add(0);  
  16.             // 格雷碼最高位的值(非0時)  
  17.             int t = 1;  
  18.             // 每一次外循環求出的是位數i+1位的格雷碼錶,其至關於長度爲i+1位的格雷碼錶的前半部分  
  19.             for (int i = 0; i < n; i++) {  
  20.                 // 求出的長度爲i+1位格雷碼錶的後半部分,前半部分由長度爲i位的格雷碼錶給出  
  21.                 for (int j = result.size() - 1; j >= 0; j--) {  
  22.                     result.add(result.get(j) ^ t);  
  23.                 }  
  24.                 // 最高位右移  
  25.                 t <<= 1;  
  26.             }  
  27.         }  
  28.         return result;  
  29.     }  
  30.   
  31.     public ArrayList<Integer> grayCode2(int n) {  
  32.         ArrayList<Integer> result = new ArrayList<Integer>();  
  33.         result.add(0);  
  34.         if (n > 0) {  
  35.             result.add(1);  
  36.         }  
  37.   
  38.         int mask = 1;  
  39.         for (int i = 2; i <= n; ++i) {  
  40.             mask *= 2;  
  41.             for (int j = result.size() - 1; j >= 0; --j) {  
  42.                 int v = result.get(j).intValue();  
  43.                 v |= mask;  
  44.                 result.add(v);  
  45.             }  
  46.         }  
  47.         return result;  
  48.     }  
  49.   
  50.     public void getGrayCode3(int bitNum) {  
  51.         for (int i = 0; i < (int) Math.pow(2, bitNum); i++) {  
  52.             int grayCode = (i >> 1) ^ i;  
  53.             System.out.println(num2Binary(grayCode, bitNum));  
  54.         }  
  55.     }  
  56.   
  57.     public String num2Binary(int num, int bitNum) {  
  58.         String ret = "";  
  59.         for (int i = bitNum - 1; i >= 0; i--) {  
  60.             ret += (num >> i) & 1;  
  61.         }  
  62.         return ret;  
  63.     }  
  64.   
  65.     public String[] GrayCode4(int n) {  
  66.   
  67.         // produce 2^n grade codes  
  68.         String[] graycode = new String[(int) Math.pow(2, n)];  
  69.   
  70.         if (n == 1) {  
  71.             graycode[0] = "0";  
  72.             graycode[1] = "1";  
  73.             return graycode;  
  74.         }  
  75.   
  76.         String[] last = GrayCode4(n - 1);  
  77.   
  78.         for (int i = 0; i < last.length; i++) {  
  79.             graycode[i] = "0" + last[i];  
  80.             graycode[graycode.length - 1 - i] = "1" + last[i];  
  81.         }  
  82.   
  83.         return graycode;  
  84.     }  
  85.   
  86.     public static StringBuffer[] get_gray(int n) {  
  87.         int length = (int) Math.pow(2, n);  
  88.         StringBuffer[] gray_code = new StringBuffer[length];  
  89.         if (n == 1) {  
  90.             gray_code[0] = new StringBuffer("0");  
  91.             gray_code[1] = new StringBuffer("1");  
  92.   
  93.         } else {  
  94.             StringBuffer[] temp = get_gray(n - 1);  
  95.             for (int i = 0; i < length; i++) {  
  96.                 // 若是用=,只是傳引用,因此對gray_code[i]插值的時候會影響temp,從而致使最後的結果錯誤。  
  97.                 // gray_code[i]=temp[i%(length/2)];  
  98.   
  99.                 if (i < (length / 2)) {  
  100.                     gray_code[i] = new StringBuffer((temp[i]));  
  101.                     gray_code[i].insert(0, "0");  
  102.                 } else {  
  103.                     gray_code[i] = new StringBuffer((temp[length - i - 1]));  
  104.                     gray_code[i].insert(0, "1");  
  105.   
  106.                 }  
  107.             }  
  108.   
  109.         }  
  110.         return gray_code;  
  111.     }  
  112.   
  113.     public static void main(String args[]) {  
  114.         GrayCode grayCode = new GrayCode();  
  115.   
  116.         cin = new Scanner(System.in);  
  117.         int n = 0;  
  118.         while (cin.hasNext()) {  
  119.             n = cin.nextInt();  
  120.             // 1.  
  121.             // System.out.println(grayCode.grayCode(n));  
  122.             // 2.  
  123. //          System.out.println(grayCode.grayCode2(n));  
  124.             // 3.  
  125.              grayCode.getGrayCode3(n);  
  126.             // 4.  
  127.             // System.out.println(grayCode.GrayCode4(n));  
  128.             // 5.  
  129.             // for (int i = 0; i < grayCode.GrayCode4(n).length; i++) {  
  130.             // System.out.println(grayCode.GrayCode4(n)[i]);  
  131.             // }  
  132.             // 6.  
  133.             // StringBuffer[] gray_code=get_gray(n);  
  134.             // for(int i=0;i<gray_code.length;i++){  
  135.             // System.out.println(gray_code[i]);  
  136.             // }  
  137.         }  
  138.     }  
相關文章
相關標籤/搜索