格雷码基础和生成的几种方法

19 篇文章 0 订阅
7 篇文章 0 订阅

1 格雷码:

1.1 格雷码引言:

在数字系统中,常要求代码按一定顺序变化。

在机器视觉里面,编码结构光也是按照一定的顺序进行变化,最常用的就是Binary,但是,二进制的纯粹的编码,由于二进制的进制关系(每个位是有权的),如果发生一个错码(在机器视觉里面,错码的发生可能是一个背景的干扰,也可能是测试物体的一个比较陡峭的轮廓变更),一个错码往往他的数字权重不是一位,比如二进制的最高为,错了一位,那么就是整个数值发生一半的变化。

去掉权重的好处就是,如果模拟量或者是采样的数据发生了一个微小的变化,在整个测量的范围内,这个微小的变化都只能变更一个格雷码数位,而不论这个测量的数值位于整个测量量程的哪个位置:

【案】在上面这个例子中,假设测量的量程为16,我们测量到7和8之间的模拟量,你看看二进制,红色表示要变更所有的数位来表示,而格雷码只要变更一位。

那么,在我们表示这个数据的时候,二进制码所有的位都必须不能出错,否则数据会大变化。而格雷码就不会出现这个问题。 

后来,弗兰克·格雷(Frank Gray,18870913-19690523)专利“Pulse Code Communication” 发明了一种新的顺序编码的方式,这个方式也是每个数值是唯一的二进制标识,但是,每个相邻的位只有一个位值的变化,这样就极大的减少了测量可能发生误差。

1.2 格雷码的定义:

在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。

格雷码是一种具有反射特性和循环特性的单步自补码,其循环和单步特性消除了随机取数时出现重大错误的可能,其反射和自补特性使得对其进行求反操作也非常方便,所以,格雷码属于一种可靠性编码,是一种错误最小化的编码方式,因此格雷码在通信和测量技术中得到广泛应用。

Gray Code(格雷码) C++多方法实现_越前浩波的博客-CSDN博客

3位格雷码的顺序编码_几种简单的编码(为什么使用ASCII码)_乔拉爵士的博客-CSDN博客

1.3 认识格雷码

下为3位元的Gray Code:Gray Code是一个数列集合,每个数使用二进位来表示,假设使用n位元来表示每个数好了,任两个数之间只有一个位元值不同

000 001 011 010 110 111 101 100

1.3.1 位元

位元就是数列的基础数是由几个二进位数来表示,就是几个位元。

上面的例子里面:000 是3个二进制数,那么就是3个位元,那么2^3 = 8,总共8个位值

0000 0001 0011 0010 0110 0111 0101 0100 1100 1101 1111 1110 1010 1011 1001 1000

上面,0000 等是4个 二进制数,那么就是4个位元,那么4^3 = 16,总共16个位值

1.3.2 (格雷)码值

001 就是一个码值


2 格雷码的生成:

2.1 方法1:按照定义排列生成格雷码

回到前面的3位元的格雷码:

000 001 011 010 110 111 101 100

如果推广到N个位元,那么码值的数量应该是2^N个

2.1.1 产生的基本规律原则和标准做法

其实就是3个步骤,

第一步,改变最右边的位元值;

第二步,改变右起第一个为1的位元的左边位元;

第三步,第四步重复第一步和第二步,直到所有的格雷码产生完毕(换句话说,已经走了(2^n) - 1 步)。

举例3位元的产生步骤:

  假设产生3位元的格雷码,原始值位 000
  第一步:改变最右边的位元值: 001
  第二步:改变右起第一个为1的位元的左边位元: 011

重复1:
  第三步:改变最右边的位元值: 010
  第四步:改变右起第一个为1的位元的左边位元: 110

重复2:
  第五步:改变最右边的位元值: 111
  第六步:改变右起第一个为1的位元的左边位元: 101

重复3:
  第七步:改变最右边的位元值: 100 #已经走了(2^n) - 1  = 7步,完成所有码字的产生。

 2.1.2 实现算法如下:

#include <stdio.h> 
#include <stdlib.h>
#define MAXBIT 20
#define TRUE 1
#define CHANGE_BIT(x) x = ((x) == '0' ? '1' : '0')
#define NEXT(x) x = (1 - (x))

    int main(void) {
        char digit[MAXBIT]; int i, bits, odd;

        printf("输入位元数:"); scanf("%d", &bits);

        for(i = 0; i < bits; i++) { digit[i] = '0';
            printf("0");
        }
        printf("\n"); odd = TRUE; while(1) {
            if(odd)
                CHANGE_BIT(digit[0]);
            else {
                // 计算第一个1的位置
                for(i = 0; i < bits && digit[i] == '0'; i++) ; if(i == bits - 1) // 最后一个Gray Code
                    break; CHANGE_BIT(digit[i+1]);
            }
            for(i = bits - 1; i >= 0; i--)
                printf("%c", digit[i]);

            printf("\n"); NEXT(odd);
        }

        return 0;
    }

方法2:

///产生n位格雷码(直接排列方法构建)
void generateGraycode2(int n)
{
	int i;
	char code[20];
 
	for (i = 0; i < n; i++)
		code[i] = '0';
	code[n] = '\0';
	printf("%s\n", code);
 
	while (1)
	{
		if (code[n - 1] == '0')
			code[n - 1] = '1';
		else
			code[n - 1] = '0';
		printf("%s\n", code);
 
		i = n - 1;
		while (code[i] == '0') i--;
		if (i == 0)
			break;
 
		if (code[i - 1] == '0')
			code[i - 1] = '1';
		else
			code[i - 1] = '0';
 
		printf("%s\n", code);
	}
}
 

方法3:

vector<int> grayCode(int n) {
        int count = 1 << n;
        vector<int> res(count,0);
        for(int i = 1 ; i < count; i ++)
        {
            int cur = res[i - 1];
            if(i % 2 == 1)
                cur  = cur ^ 1;
            else
            {
                int k = 0;
                while(((cur >> k) & 1) == 0) 
                    k ++;
                cur = cur ^ (1 << (k + 1));
            }
            res[i] = cur;
        }
        return res;
    }

  C++经典算法题-格雷码(Gray Code)_逍遥云恋-CSDN博客_c++格雷码


2.2 方法2:通过二进制和格雷码的转码规律生成:

格雷码(从零基础讲解,C++版)_不负AC不负卿的博客-CSDN博客

 Gray Code(格雷码) C++多方法实现_越前浩波的博客-CSDN博客

观察上表,我们可以得出格雷码和二进制码之间的基本换算逻辑如下:

 

 2.2.1 二进制转码为格雷码(编码)

原理:如果二进制码字的第 i 位和 i+1 位(从右边开始数)相同,则对应的格雷码的第i位为0,否则为1(当i+1=n时,二进制码字的第n位被认为是0,即第n-1位不变)

/*编码模板 */
#include <iostream>
using namespace std;

int gray_encode(int num)
{
	return num ^ (num >> 1);		// >>是位移运算符,^是异或运算符
}

方法2:

class Solution {
public:
    vector<int> grayCode(int n) {
        int num = 1 << n;
        vector<int> ret;
        ret.reserve(num);
        
        for(int i = 0; i < num; i++)
        {
            ret.push_back(i ^ (i >> 1));
        }
        
        return ret;
    }
};

 

 2.2.1 格雷码转成二进制(解码)

 

 原理:从左边第二位起,将每位与左边一位解码后的值进行异或,作为该位解码后的值(最左边一位依然不变),直到最低位。

 这里因为转成二进制的权重的位,所以要取对数log,并以2为底,用数值去除log2

/*解码模板 */
#include<math.h>		// log对数函数需要用到的头文件
#include <iostream>
using namespace std;

int gray_decode(int num)
{
	int head;
	if(!num) return 0;
	head = 1 << int(log(num) / log(2));	//C++没有直接以2为底的对数,我们创造一个以2为底的对数
   return head + gray_decode((num^head) ^ (head>>1));


2.3 镜像排列生成格雷码(对称法)

格雷码编解码学习(一):格雷码编码原理与C++代码实现_Color Space的博客-CSDN博客

上面这个列表,分别给出位元为1,2,3的三组格雷码,其中灰色的部分是镜像分割线,黑色的格雷码码字和蓝色的格雷码码字是针对镜像分割线对称的

算法原理:

C++ 递归产生格雷码_永远在路上啊的博客-CSDN博客

当只有一位的时候,格雷码要么是0,要么是1.
        如果有两位的时候,格雷码是首先由  0
                                    1
        然后镜像对称得到后两位:            1
                                    0
        然后再在上面两行前面加上0,下面两行加1得到:
                                    00
                                    01
                                    11
                                    10
        这就是二位的格雷码产生的过程。所以可以使用递归的方法去产生格雷码:
                        {    0,1                                n==1
                f(n)  = {   f(n-1)                            n >=2
                        {    对称翻转,然后上一半加0,后一半加1

2.3.1 实现算法1,C++,二维数组

template<typename T,unsigned int N> void recursive::grayCodeGeneration(T (*list)[N],std::size_t size)const{
    if(size==1){//最后需要翻转输出
        list[0][size-1]=0;
        list[1][size-1]=1;
    }
    else{
        grayCodeGeneration(list,size-1);
        //下面做的是对称翻转
        for(int i= std::pow(2,size-1),j=std::pow(2,size)-1;i < std::pow(2,size);++i)
            for(int k = 0;k<N;k++)
                list[i][k] = list[j-i][k];//对应的逆序的行进行保存,这个时候用到的是
                                        //需要逆序保存的两个行之和是pow(2,size)-1;
        //这一步做的是前一部分加0,后一步分加1
        for(int i = 0;i < std::pow(2,size);i++){
            if(i <= (std::pow(2,size)/2-1))
                list[i][size-1]=0;
            else
                list[i][size-1]=1;
        }
    }
}

pow 就是求某数的指数的函数 

2.3.2 实现算法2,C++,

 C++ 生成代码如下:

#include <iostream>
#include <vector>
#include <string>
 
using namespace std;
 
///产生n位格雷码(镜像排列方法构建)
vector<string> generateGraycode(int n)
{
	vector<string> nGraycode;
	if (n == 1)
	{
		nGraycode.push_back("0");
		nGraycode.push_back("1");
	}
	else
	{
		vector<string> mid_ans = generateGraycode(n - 1);
		for (vector<string>::iterator iter = mid_ans.begin(); iter != mid_ans.end(); ++iter)
		{
			string temp = *iter;
			temp.insert(0, "0");
			nGraycode.push_back(temp);
		}
		for (int i = mid_ans.size() - 1; i >= 0; --i)
		{
			string temp = mid_ans[i];
			temp.insert(0, "1");
			nGraycode.push_back(temp);
		}
	}
	return nGraycode;
}
 
int main()
{
	vector<string>nGraycode;
	nGraycode = generateGraycode(3);
	for(int i = 0; i < nGraycode.size(); i++)
		cout << nGraycode.at(i) << endl;
	return 0;
}

 C++ 另一种表述为:

#include <iostream>
#include<vector>
using namespace std;

vector<string> getGray(int n) {
        // write code here
    vector<string> result;
    if(n == 1){
      result.push_back("0");
      result.push_back("1");
       return result;
    }else{
        result = getGray(n-1);
        int currentsize = (int)result.size();
        for(int i = 0; i < currentsize; i++){
            result.push_back(result.at(i));
        }
        for(int i = 0; i < currentsize; i++){
                 result.at(i) = "0" + result.at(i);
        }
        for(int i = currentsize; i < (int)result.size(); i++){
            result.at(i) = "1" + result.at(i);
        }
         return result;
        }
    }
int main(){
    vector<string> v;
    v = getGray(2);
    for(int i = 0; i < v.size(); i++){
        cout << v.at(i) << " ";
    }
    cout << endl;
    return 0;
}

实现算法另一种表述、

[C++]LeetCode: 86 Gray Code (格雷码)_cindy_niu的专栏-CSDN博客

class Solution {
public:
    vector<int> grayCode(int n) {
        vector<int> ret{0};
        
        for(int i = 0; i < n; i++)
        {
            int curCnt = ret.size();
            //把当前数字按照逆序顺序添加到ret中
            while(curCnt)
            {
                curCnt--;
                int curNum = ret[curCnt];
                curNum += (1 << i);
                ret.push_back(curNum);
            }
        }
        
        return ret;
    }
};

 

2.3.3 实现算法3,C,二维数组

c 语言表述:

算法实验-格雷码_中位数问题 分治法-CSDN博客_格雷码分治

//用分治策略设计一个算法对任意的n构造相应的Gray码。
#include<iostream>
#include<fstream>
using namespace std;
//构造n位格雷码,格雷码数m个
//利用格雷码除第一位外对称的原理
char** countgary(int n)
{
	int  m = 1;
	for (int a = 0; a < n; a++)
		m = m * 2;
	char** garycode = new char *[m];
	for (int a = 0; a < m; a++)
		garycode[a] = new char[n];
	if (n == 1)
	{
		garycode[0][0] ='0';
		garycode[1][0] ='1';
		return garycode;
	}//1的格雷码为0和1
	char** temp = countgary(n - 1);
	for (int a = 0; a < m / 2; a++)
	{
		garycode[a][0] = '0';
		garycode[a + m / 2][0] = '1';
		//m个格雷码前一半的第一位为0,后一半第一位为1
		for (int b = 1; b < n; b++)
		{
			garycode[a][b] = temp[a][b - 1];
			garycode[m-a-1][b] = temp[a][b - 1];
			//将n-1的格雷码呈上下对称放在n格雷码的前半段和后半段
		}
	}
	return garycode;
}
int main()
{
	ifstream f1("C:\\Data\\3.txt");
	ofstream f2("C:\\Data\\4.txt");
	int n; int m = 1;
	f1 >> n;
	for (int a = 0; a < n; a++)
		m = m * 2;
	char **garycode = countgary(n);
	for (int a = 0; a < m; a++)
	{
		for (int b = 0; b < n; b++)
		{
			f2 << garycode[a][b] << "\t";
			cout << garycode[a][b] << "\t";
		}
		cout << "\n";
		f2 << "\n";
	}
	f1.close();
	f2.close();
	return 0;
}

格雷码的算法实现_(っ°Д°)っ  #-CSDN博客_格雷码算法

2.4 正序逆序生成格雷码(对称法)

在正序的基础上将1左移n-1位,再加在逆序上,即得green code 格雷码。

算法返回的是10进制的值

class Solution {
public:
    vector<int> grayCode(int n) {
        vector<int> res;
        int c=1;
        res.push_back(0);
        for(int i=0;i<n;i++){
            for(int j=res.size()-1;j>=0;j--)
                res.push_back(res[j]+c);
            c<<=1;
        }
        return res;
    }
    
};

C++经典算法题-格雷码(Gray Code)_逍遥云恋-CSDN博客_c++格雷码

(1条消息) [C++]LeetCode: 86 Gray Code (格雷码)_cindy_niu的专栏-CSDN博客


本文重要参考:

C++经典算法题-格雷码(Gray Code)_逍遥云恋-CSDN博客_c++格雷码

  • 54
    点赞
  • 228
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 在MATLAB中,要生成格雷码光栅,可以通过以下步骤进行操作: 1. 首先,确定需要生成格雷码光栅的位数,假设为n位。 2. 创建一个长度为2^n的向量gray_code,用于存储生成格雷码光栅序列。 3. 初始化gray_code的第一个元素为0。 4. 通过循环从第二个元素开始,根据格雷码的特性生成剩余的格雷码。 5. 在每次循环中,将当前元素与上一个元素进行异或操作,得到新的格雷码。 6. 将新生成格雷码添加到gray_code中。 7. 重复步骤5和6,直到生成格雷码数目达到2^n。 8. 最后,返回gray_code向量即可得到生成格雷码光栅序列。 下面是MATLAB代码实现的示例: ```matlab function gray_code = generate_gray_code(n) gray_code = zeros(1, 2^n); % 创建一个大小为2^n的零向量 gray_code(1) = 0; % 初始化第一个元素为0 for i = 2:2^n gray_code(i) = bitxor(gray_code(i-1), bitshift(1, mod(i-1, n))); % 根据格雷码特性生成剩余的格雷码 end end ``` 调用该函数,可以生成任意位数的格雷码光栅,例如调用`generate_gray_code(3)`可以得到3位格雷码光栅序列[0 1 3 2 6 7 5 4]。 希望以上回答对您有帮助,如有任何问题,请随时追问。 ### 回答2: 格雷码光栅是一种特殊的二进制编码方式,其相邻的两个数值只有一位不同。在Matlab中,我们可以使用以下几个步骤生成格雷码光栅。 步骤1:确定所需的编码位数n。格雷码光栅的位数n决定了编码的精度,即格雷码光栅的可分辨率。 步骤2:创建一个长度为2^n的数组grayCode,用于存储生成格雷码光栅。 步骤3:生成格雷码光栅。格雷码光栅的生成可以通过递归的方法实现,如下所示: ```matlab function grayCode = generateGrayCode(n) if n == 1 grayCode = [0, 1]; else prevGrayCode = generateGrayCode(n-1); grayCode = [zeros(1, length(prevGrayCode)), ones(1, length(prevGrayCode))]; grayCode = [prevGrayCode, fliplr(grayCode)]; end end ``` 其中,prevGrayCode是前一个更低位的格雷码光栅。 步骤4:调用上述函数生成所需位数的格雷码光栅。 ```matlab n = 4; % 设定位数为4 grayCode = generateGrayCode(n); disp(grayCode); ``` 在这个例子中,我们生成了4位的格雷码光栅,结果为: ``` 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 1 1 0 0 1 1 1 0 1 0 1 0 1 0 0 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 0 0 1 1 0 0 0 ``` 这就是4位格雷码光栅的生成结果。每一行都代表一个格雷码光栅,从第一行到最后一行,只有一位数值发生变化。 ### 回答3: MATLAB可以通过以下步骤生成格雷码光栅。 1. 首先,需要定义格雷码的位数(bit)。可以根据需求选择任意的bit数,例如8位、16位等。 2. 接下来,使用MATLAB内置函数gray2bin将格雷码转换为二进制码。这个函数将二进制数组转换为格雷码数组。 3. 生成二进制码的方法是使用dec2bin函数将整数转换为二进制码。这个函数可以将十进制数转换为指定位数的二进制码。 4. 创建一个矩阵来保存生成格雷码光栅。可以使用MATLAB的矩阵操作来生成矩阵。 5. 使用循环来遍历格雷码数组,并将格雷码复制到光栅矩阵的每一行。 6. 最后,将光栅矩阵保存为图像文件。可以使用imwrite函数将矩阵保存为图像文件,以便在其他程序中使用。 这样,就可以生成一个格雷码光栅。可以调整步骤中的参数和代码,以满足具体需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Franklin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值