格雷码

原题

  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:

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的格雷码表。

解题思路

  递归生成码表
  这种方法基于格雷码是反射码的事实,利用递归的如下规则来构造:
  1位格雷码有两个码字
  (n+1)位格雷码中的前2n个码字等于n位格雷码的码字,按顺序书写,加前缀0
  (n+1)位格雷码中的后2n个码字等于n位格雷码的码字,按逆序书写,加前缀1

代码实现

算法实现类

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),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码反射码

 

那么格雷码有什么规律呢?当用一个二进制表示格雷码时,

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. 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

 

如果按照这个规则来生成格雷码,是没有问题的,但是这样做太复杂了。如果仔细观察格雷码的结构,我们会有以下发现:

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位元格雷码产生的。

 

如果能够理解上面的部分,下面部分的代码实现就很容易理解了。

[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.     }  

 

 

转载于:https://my.oschina.net/u/2822116/blog/808490

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值