Gray码是一种数字编码方式,可以使相邻的两个数之间只有一位的差别。
构造格雷码的方式很简单,首先列出
0
1
以底部为水平轴在轴下方写出轴上方的反射,并在轴上方数字左边都加0,轴下方数字都加1:
00
01
11
10
这只是2位的表示0~3的Gray码,重复相同操作还可以得到:
000
001
011
010
110
111
101
100
通过这个方法,我们可以列出前2^n项数字的格雷码,用代码可以写成:
unsigned char *getGrayCodeList(int n) { unsigned char *num = (unsigned char *)malloc(sizeof(unsigned char) * (1 << n)); memset(num, 0, sizeof(unsigned char) * (1 << n)); int i, j, count = 2; num[0] = 0; num[1] = 1; for (i = 1; i < n; i++) { for (j = 0; j < count; j++) num[(count << 1) - 1 - j] = num[j]; count <<= 1; for (j = 0; j < (count >> 1); j++) *(num + j) &= ~(1 << i); for (;j < count; j++) *(num + j) |= (1 << i); } return num; }
假设有4位二进制码abcd和格雷码efgh
因为二进制码abcd和Gray码之间有如下结论:
e = a
f = a ^ b
g = b ^ c
h = c ^ d
a = e
b = e ^ f
c = e ^ f ^ g
d = e ^ f ^ g ^ h
所以有以下一些运算
把二进制码转换为格雷码:
unsigned char bin2gray(unsigned char num, int n) { unsigned char retval = 0; int i; retval |= (num & (1 << (n - 1))); for (i = n - 1; i > 0; i--) { retval |= ((((num & (1 << i)) >> i) ^ ((num & (1 << (i - 1))) >> (i - 1))) << (i - 1)); } return retval; }
Gray码转换为二进制码:
unsigned char gray2bin(unsigned char num, int n) { unsigned char retval = 0; int i, last; retval |= (num & (1 << (n - 1))); last = (num >> (n - 1)) & 1; for (i = n - 2; i >= 0; i--) { last ^= ((num >> i) & 1); retval |= last << i; } return retval; }
Gray码递增最简单的方法就是转换为二进制码递增后再转换为Gray码:
void gray_inc(unsigned char *num, int n) { *num = bin2gray(gray2bin(*num, n) + 1, n); }
也有以下两种方法:
void gray_inc2(unsigned char *num, int n) { unsigned char b = gray2bin(*num, n) + 1; *num = (b ^ (b >> 1)); } void gray_inc3(unsigned char *num, int n) { unsigned char b = gray2bin(*num, n); *num = (*num ^ (~b & (b + 1))); }
参考:《Hacker's Delight》by Henry S.Warren, Jr.