格雷码Gray Code详解
格雷码简介
在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。格雷码(Gray Code)又称Grey Code、葛莱码、格莱码、戈莱码、循环码、反射二进制码、最小差错码等。
格雷码有多种编码形式
十进制数 | 4位自然二进制码 | 4位典型格雷码 |
十进制余三格雷码
| 十进制空六格雷码 | 十进制跳六格雷码 | 步进码 |
---|---|---|---|---|---|---|
0
|
0000
|
0000
|
0010
|
0000
|
0000
|
00000
|
1
|
0001
|
0001
|
0110
|
0001
|
0001
|
00001
|
2
|
0010
|
0011
|
0111
|
0011
|
0011
|
00011
|
...
为什么要使用格雷码?
格雷码是一种具有反射特性和循环特性的单步自补码,其循环和单步特性消除了随机取数时出现重大错误的可能,其反射和自补特性使得对其进行求反操作也非常方便,所以,格雷码属于一种可靠性编码,是一种错误最小化的编码方式,因此格雷码在通信和测量技术中得到广泛应用。
格雷码属于可靠性编码,是一种错误最小化的编码方式。因为,虽然自然二进制码可以直接由数/模转换器转换成模拟信号,但在某些情况,例如从十进制的3转换为4时二进制码的每一位都要变,能使数字电路产生很大的尖峰电流脉冲。而格雷码则没有这一缺点,它在相邻位间转换时,只有一位产生变化。它大大地减少了由一个状态到下一个状态时逻辑的混淆。由于这种编码相邻的两个码组之间只有一位不同,因而在用于方向的转角位移量-数字量的转换中,当方向的转角位移量发生微小变化(而可能引起数字量发生变化时,格雷码仅改变一位,这样与其它编码同时改变两位或多位的情况相比更为可靠,即可减少出错的可能性。
在数字系统中,常要求代码按一定顺序变化。例如,按自然数递增计数,若采用8421码,则数0111变到1000时四位均要变化,而在实际电路中,4位的变化不可能绝对同时发生,则计数中可能出现短暂的其它代码(1100、1111等)。在特定情况下可能导致电路状态错误或输入错误。使用格雷码可以避免这种错误。
格雷码是一种绝对编码方式,典型格雷码是一种具有反射特性和循环特性的单步自补码,它的循环、单步特性消除了随机取数时出现重大误差的可能,它的反射、自补特性使得求反非常方便。
由于格雷码是一种变权码,每一位码没有固定的大小,很难直接进行比较大小和算术运算,也不能直接转换成液位信号,要经过一次码变换,变成自然二进制码,再由上位机读取。
典型格雷码是一种采用绝对编码方式的准权码,其权的绝对值为2^i-1(设最低位i=1)。
格雷码的十进制数奇偶性与其码字中1的个数的奇偶性相同。
应用
格雷氏编码与相位移在三维曲面量测:利用格雷码投射在微型曲面做量测 一个非接触式、投影的方法光学测量。
在化简逻辑函数时,可以通过按格雷码排列的卡诺图来完成。
角度传感器:汽车制动系统有时需要传感器产生的数字值来指示机械位置。如图是编码盘和一些触点的概念图,根据盘转的位置,触点产生一个3位二进制编码,共有8个这样的编码。盘中暗的区域与对应的逻辑1的信号源相连;亮的区域没有连接,触点将其解释为逻辑0。使用格雷码对编码盘上的亮暗区域编码,使得其连续的码字之间只有一个数位变化。这样就不会因为器件制造的精确度有限,而使得触点转到边界位置而出现错误编码。
九连环问题:中国的古老益智玩具九连环有着和格雷码完全相同的数学模式,外国一款名为spin out的玩具也是运用相同的数学模式。智力玩具九连环的状态 变化符合格雷码的编码规律,汉诺塔的解法也与格雷码有关。九连环中的每个环都有上下两种状态,如果把这两种状态用0/1来表示的话,这个状态序列就会形成一种循环二进制编码(格雷码)的序列。所以解决九连环问题所需要的状态变化数就是格雷码111111111所对应的十进制数341。
二进制格雷码的生成
直接排列
生成二进制格雷码方式1:以二进制为0值的格雷码为第零项,第一项改变最右边的位元,第二项改变右起第一个为1的位元的左边位元,第三、四项方法同第一、二项,如此反复,即可排列出n个位元的格雷码。
000
001
011
010
110
111
101
100
[格雷码维基百科]
其它方法
利用卡诺图生成
-
将卡诺图变量分为两组,变量数目相近(最好相等)
-
以逻辑变量高位在左低位在右建立卡诺图
-
从卡诺图的左上角以之字形到右上角最后到左下角遍历卡诺图,依次经过格子的变量取值即为典型格雷码的顺序
利用二进制码转换
转换方法参考下面吧。格雷码和二进制码的转换
自然二进制码与格雷码的对照表:
十进制数 | 自然二进制数 | 格雷码 | 十进制数 | 自然二进制数 | 格雷码 |
0 | 0000 | 0000 | 8 | 1000 | 1100 |
1 | 0001 | 0001 | 9 | 1001 | 1101 |
2 | 0010 | 0011 | 10 | 1010 | 1111 |
3 | 0011 | 0010 | 11 | 1011 | 1110 |
4 | 0100 | 0110 | 12 | 1100 | 1010 |
5 | 0101 | 0111 | 13 | 1101 | 1011 |
6 | 0110 | 0101 | 14 | 1110 | 1001 |
7 | 0111 | 0100 | 15 | 1111 | 1000 |
二进制码转换成二进制格雷码
二进制码转换成二进制格雷码,其法则是保留二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。
二进制码 ----> 格雷码(编码):从最右边一位起,依次将每一位与左边一位异或(XOR),作为对应格雷码该位的值,最左边一位不变(相当于左边是0)。
Note: 这样做可行的原因,是因为二进制码每次+1时最多只有一个相邻的两个bit对的异或值会发生改变。
公式表示:G:格雷码 B:二进制码
整个数G(N) = (B(n) >> 1) XOR B(n)
格雷码转换成二进制码
二进制格雷码转换成二进制码,其法则是保留格雷码的最高位作为自然二进制码的最高位,而次高位自然二进制码为高位自然二进制码与次高位格雷码相异或,而自然二进制码的其余各位与次高位自然二进制码的求法相类似。
公式表示:
格雷码转换为二进制码算法有以下几种表述形式:
表述一:
二进制格雷码为Gn-1Gn-2...G2G1G0
自然二进制码为Bn -1Bn-2...B2B1B0
其中:最高位保留 Bn-1=Gn-1
其他各位 Bi-1=Gi-1 xor Bi ,i=1,2,...,n-1
表述二:
Bi = ˆG[n-1:i]=G[n-1]ˆG[n-2]ˆ..ˆG[i],i=0,1,...,n-1
表述三:
Bi = ˆ(G>>i),i=0,1,...,n-1
[格雷码(Gray Code)转二进制码(Binary Code)]
二进制格雷码字符串生成
c++ stl递归和非递归代码
vector<string> gray0(int n) { /* * 格雷码字符串的直接排列递归实现 * 思路:1、获得n-1位生成格雷码的数组 * 2、由于n位生成的格雷码位数是n-1的两倍,故只要在n为格雷码的前半部分加0,后半部分加1即可。 */ if (n == 0) return vector<string>{"0"}; else if (n == 1) { return vector<string>({"0", "1"}); } else { vector<string> new_gray_code; vector<string> gray_code = gray0(n - 1); // vector<string> gray_code = vector<string>({"0", "1"}); vector<string>::iterator gc_it; for (gc_it = gray_code.begin(); gc_it != gray_code.end(); gc_it++) new_gray_code.push_back("0" + *gc_it); vector<string>::reverse_iterator gc_rit; for (gc_rit = gray_code.rbegin(); gc_rit != gray_code.rend(); gc_rit++) new_gray_code.push_back("1" + *gc_rit); return new_gray_code; } } vector<string> gray1(int n) { /* * 格雷码字符串的镜射排列非递归实现 */ if (n == 0) return vector<string>{"0"}; vector<string> gray_code = vector<string>({"0", "1"}); while (--n) { vector<string> new_gray_code; vector<string>::iterator gc_it; for (gc_it = gray_code.begin(); gc_it != gray_code.end(); gc_it++) new_gray_code.push_back("0" + *gc_it); vector<string>::reverse_iterator gc_rit; for (gc_rit = gray_code.rbegin(); gc_rit != gray_code.rend(); gc_rit++) new_gray_code.push_back("1" + *gc_rit); gray_code = new_gray_code; } return gray_code; }
非stl c++代码[格雷码那点事——递归非递归实现][格雷码的实现]
public void getGrayCode(int bitNum){
for(int i = 0; i < (int)Math.pow(2, bitNum); i++){
int grayCode = (i >> 1) ^ i;
System.out.println(num2Binary(grayCode, bitNum));
}
}
public String num2Binary(int num, int bitNum){
String ret = "";
for(int i = bitNum-1; i >= 0; i--){
ret += (num >> i) & 1;
}
return ret;
}