格雷码(Gray Code)与8421二进制码之间的转换算法 (LeetCode89)

Posted Kant101

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了格雷码(Gray Code)与8421二进制码之间的转换算法 (LeetCode89)相关的知识,希望对你有一定的参考价值。

1. 格雷码简介

格雷码跟8421码一样,也是一种对数字进行二进制编码的方法,只是编码方法跟常见的8421二进制编码方法不一样。

格雷码序列中,相邻的两个数字的二进制编码只有一位是不一样的,这种编码方式常用于较复杂的电路中。大家都知道二进制的 0/1 代表了电路的 开/关 ,例如n位的二进制编码,有很多情况下电路需要遍历 0 ~ 2^n 的情况,在电路实现中就是用n位的电路的开关依次转换来实现。

由于8421二进制编码的 0 ~ 2^n 的序列,相邻的两个数的二进制相差会出现多位不一样,因此电路在遍历时就需要同事变更多位电路开关,而这种操作是需要时间的,在这个过程中就有可能出现中间的状态,从而出现错误的编码。
例如:
n = 3 的 8421 编码

i8421编码
0000
1001
2010
3011
4100
5101
6110
7111

相邻两个数之间的二进制编码相差的不止1位,就会出现上面说的情况。而格雷码编码中,相邻的两个数的二进制编码只有1位是不一样的,因此在物理电路的变换中只涉及到一位电路的开关操作,即操作是原子的,不会出现中间过程,也就不会出现错误编码了,因此在某些场景的电路信号中被广泛运用。

2. 格雷码与8421二进制码之间的转换

n = 3 为例,8421码与格雷码之间的对应关系如下:

格雷码也是以全0开头的

i8421编码格雷码
0000000
1001001
2010011
3011010
4100110
5101111
6110101
7111100

2.1 8421码 ==> 格雷码

算法
1、8421码最左边一位不变,保留下来成为格雷码的最左边一位;
2、从左边第二位开始,将8421码的每一位与它左边的一位相 异或 得到对应位的格雷码;

代码实现

public static int toGrayCode(int i) 
	return i ^ (i >> 1);

ii - 1 异或,也即将 i 的每一位与它的左边一位相异或。右移操作左边是补的0,0与任何数异或等于其本身,因此最左边一位被保留了。

2.2 格雷码 ==> 8421码

算法
1、格雷码的最左边一位保持不变,保留下来成为8421码的最左边一位;
2、从左边第二位开始,将8421码的每一位与前一位计算得到的8421码 异或 得到当前的8421码;

解释

8421码转格雷码是通过 i ^ (i - 1) 实现的,要想将格雷码转换为8421码,我们知道,异或运算有如下性质:
(a ^ b) ^ b = a ^ (b ^ b) = a

因此,可以得到 (i ^ (i >> 1)) ^ (i >> 1) = i ^ ((i >> 1) ^ (i >> 1)) = i
所以在上面的算法中在计算当前位的8421码时,需要将当前位的格雷码与上一位计算得到的8421码异或。

代码实现

public static int toBinaryCode(int grayCode, int n) 
    String grayCodeStr = Integer.toBinaryString(grayCode);
    if (grayCodeStr.length() < n) 
        // 补齐0,使得二进制编码有n位
        int bitCnt = grayCodeStr.length();
        for (int i = bitCnt; i < n; i++) 
            grayCodeStr = "0" + grayCodeStr;
        
    
    StringBuilder binary = new StringBuilder();
    binary.append(grayCodeStr.charAt(0)); //最左边一位不变
    for (int i = 1; i < n; i++) 
        char cur = grayCodeStr.charAt(i);
        char preBinary = binary.charAt(i - 1);
        int tmp = (cur - '0') ^ (preBinary - '0');
        if (tmp == 0) binary.append('0');
        else binary.append('1');
    
    return Integer.parseInt(binary.toString(), 2);

2.3 测试

public class Demo 
    public static void main(String[] args) 
        int n = 3;
        int powN = (int) Math.pow(2, n);
        int[] grayCode = new int[powN];
        System.out.println("========= Binary to GrayCode ==========");
        for (int i = 0; i < powN; i++) 
            grayCode[i] = toGrayCode(i);
            System.out.print(grayCode[i] + " ");
        
        System.out.println();
        System.out.println("========= GrayCode to Binary ==========");
        for (int i = 0; i < powN; i++) 
            int binary = toBinaryCode(grayCode[i], n);
            System.out.print(binary + " ");
        
        System.out.println();
    

    public static int toGrayCode(int i) 
        return i ^ (i >> 1);
    

    public static int toBinaryCode(int grayCode, int n) 
        String grayCodeStr = Integer.toBinaryString(grayCode);
        if (grayCodeStr.length() < n) 
            // 补齐0,使得二进制编码有n位
            int bitCnt = grayCodeStr.length();
            for (int i = bitCnt; i < n; i++) 
                grayCodeStr = "0" + grayCodeStr;
            
        
        StringBuilder binary = new StringBuilder();
        binary.append(grayCodeStr.charAt(0)); //最左边一位不变
        for (int i = 1; i < n; i++) 
            char cur = grayCodeStr.charAt(i);
            char preBinary = binary.charAt(i - 1);
            int tmp = (cur - '0') ^ (preBinary - '0');
            if (tmp == 0) binary.append('0');
            else binary.append('1');
        
        return Integer.parseInt(binary.toString(), 2);
    

可以看到二进制与格雷码之间是可以正确转换的。


参考文献

[1] https://baike.baidu.com/item/%E6%A0%BC%E9%9B%B7%E7%A0%81/6510858?fr=aladdin#5
[2] https://leetcode.com/problems/gray-code/

以上是关于格雷码(Gray Code)与8421二进制码之间的转换算法 (LeetCode89)的主要内容,如果未能解决你的问题,请参考以下文章

[LeetCode]Gray Code

Gray Code

089 Gray Code 格雷编码

格雷码那点事——递归非递归实现

hdu5375 Gray code(DP)

递归实现生成Grey码