什么是格雷码?
下面一段来自百度百科:
在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(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的格雷码组成。
上面三点规律可以从下图看出:
以上可知,只要用个递归就可实现。
下面是实现代码:
import java.io.*;
import java.util.Scanner;
public class N_gray {
public static void main(String[] args){
//难道java中输入一个整数要经历很多东西吗?
Scanner sc=new Scanner(System.in);
String n=sc.nextLine();
int N=Integer.parseInt(n);
StringBuffer[] gray_code=get_gray(N);
for(int i=0;i<gray_code.length;i++){
System.out.println(gray_code[i]);
}
}
public static StringBuffer[] get_gray(int n){
int length=(int)Math.pow(2, n);
StringBuffer[] gray_code=new StringBuffer[length];
if(n==1){
gray_code[0]=new StringBuffer("0");
gray_code[1]=new StringBuffer("1");
}
else{
StringBuffer[] temp=get_gray(n-1);
for(int i=0;i<length;i++){
//如果用=,只是传引用,所以对gray_code[i]插值的时候会影响temp,从而导致最后的结果错误。
//gray_code[i]=temp[i%(length/2)];
if(i<(length/2)){
gray_code[i]=new StringBuffer((temp[i]));
gray_code[i].insert(0, "0");
}
else{
gray_code[i]=new StringBuffer((temp[length-i-1]));
gray_code[i].insert(0, "1");
}
}
}
return gray_code;
}
}
格雷码还有一种实现方式是根据这个公式来的G(n) = B(n) XOR B(n+1), 这也是格雷码和二进制码的转换公式。代码如下:
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;
}
汇总:
//格雷码生成
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class GrayCode {
private static Scanner cin;
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;
}
public ArrayList<Integer> grayCode2(int n) {
ArrayList<Integer> result = new ArrayList<Integer>();
result.add(0);
if (n > 0) {
result.add(1);
}
int mask = 1;
for (int i = 2; i <= n; ++i) {
mask *= 2;
for (int j = result.size() - 1; j >= 0; --j) {
int v = result.get(j).intValue();
v |= mask;
result.add(v);
}
}
return result;
}
public void getGrayCode3(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;
}
public String[] GrayCode4(int n) {
// produce 2^n grade codes
String[] graycode = new String[(int) Math.pow(2, n)];
if (n == 1) {
graycode[0] = "0";
graycode[1] = "1";
return graycode;
}
String[] last = GrayCode4(n - 1);
for (int i = 0; i < last.length; i++) {
graycode[i] = "0" + last[i];
graycode[graycode.length - 1 - i] = "1" + last[i];
}
return graycode;
}
public static StringBuffer[] get_gray(int n) {
int length = (int) Math.pow(2, n);
StringBuffer[] gray_code = new StringBuffer[length];
if (n == 1) {
gray_code[0] = new StringBuffer("0");
gray_code[1] = new StringBuffer("1");
} else {
StringBuffer[] temp = get_gray(n - 1);
for (int i = 0; i < length; i++) {
// 如果用=,只是传引用,所以对gray_code[i]插值的时候会影响temp,从而导致最后的结果错误。
// gray_code[i]=temp[i%(length/2)];
if (i < (length / 2)) {
gray_code[i] = new StringBuffer((temp[i]));
gray_code[i].insert(0, "0");
} else {
gray_code[i] = new StringBuffer((temp[length - i - 1]));
gray_code[i].insert(0, "1");
}
}
}
return gray_code;
}
public static void main(String args[]) {
GrayCode grayCode = new GrayCode();
cin = new Scanner(System.in);
int n = 0;
while (cin.hasNext()) {
n = cin.nextInt();
// 1.
// System.out.println(grayCode.grayCode(n));
// 2.
// System.out.println(grayCode.grayCode2(n));
// 3.
grayCode.getGrayCode3(n);
// 4.
// System.out.println(grayCode.GrayCode4(n));
// 5.
// for (int i = 0; i < grayCode.GrayCode4(n).length; i++) {
// System.out.println(grayCode.GrayCode4(n)[i]);
// }
// 6.
// StringBuffer[] gray_code=get_gray(n);
// for(int i=0;i<gray_code.length;i++){
// System.out.println(gray_code[i]);
// }
}
}
}