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

1. 格雷码简介

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

格雷码序列中,相邻的两个数字的二进制编码只有一位是不一样的,这种编码方式常用于较复杂的电路中。大家都知道二进制的 1/0 代表了电路的 开/关 ,例如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码转格雷码是通过 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/

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值