前段时间做结构光三维重建的时候用到了格雷码编码方法,这里正好做一下总结。

这里讨论的是典型的二进制格雷码(Binary Gray Code),简称格雷码,由贝尔电话实验室研究物理学家Frank Gray提出。Frank Gray 1969年过世,这里所提的Gray码是他在1940年研究出来的,用来在PCM(Puslue Code Modulation)方法传送信号时避免错误。1953年3月17日,Gray取得美国专利,这是格雷码第一次出版的日期。

在一组数的编码中,若任意两个相信的代码只有一位二进制数不同,则称这种编码为格雷码,另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。前面提到格雷码的提出是为了避免讯号传送错误,原理是什么呢?很简单,在数字系统中,常要求代码按一定顺序变化。比如数字3(二进制011)切换到相信的数字4(二进制100),装置的三个位元都要转换,但是在实际电路中,3位变换不可能绝对同时发生,则计数中可能出现短暂的其它代码,可能导致电路状态错误。格雷码可以避免这种错误。

1

2

3

4

5

6

7

8十进制 格雷码 二进制

0 000 000

1 001 001

2 011 010

3 010 011

4 110 100

5 111 101

6 101 110

下面谈一下一个喜闻乐见的问题:格雷码生成。

格雷码有一种天然的递归算法:如果要求n位的格雷码,那就先n-1位的;假设G(n-1)表示所有n-1位的格雷码,于是n位格雷码可以用下面的两步构造出来,第一步,把G(n-1)中各个元素左边都加一个0.第二步,把G(n-1)中各个元素的顺序反过来排列,并且在前面都加一个1.由此得到的合集就是格雷码G(n)。

1

2

3

4

5G(1)={0,1}

G(2)=0{0,1}∪1{1,0}={00,01,11,10}

G(3)={000,001,011,010,110,111,101,100}

G(4)={0000,0001,0011,0010,0110,0111,0101,0100,

1100,1101,1111,1110,1010,1011,1001,1000}

证明也很简单,数学归纳法。当n=1时,{0,1}。如果比n-1小或等于的格雷码都可以用那两条规则构造出来。对于G(n)的前半部分,把0去除显然满足格雷码的性质,加上0同样满足;后半部分亦然。接口部分差异在于第一位的0和1,即只相差1位。G(n)的元素个数为G(n-1)的两倍,即2的n次方,完备性。证毕。简单用C++实现如下。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27vector generateGraycode(int n)

{

vector ans;

if(n==1)

{

ans.push_back("0");

ans.push_back("1");

}

else

{

vector mid_ans = generateGraycode(n-1);

for(vector::iterator iter=mid_ans.begin(); iter!=mid_ans.end(); ++iter)

{

string temp = *iter;

temp.insert(0, "0");

ans.push_back(temp);

}

for(int i=mid_ans.size()-1; i>=0; --i)

{

string temp = mid_ans[i];

temp.insert(0, "1");

ans.push_back(temp);

}

}

return ans;

}

另一种方法直接生成。稍微仔细观察一下不难发现,最后一位交替改变(0变1,1变0),在每两步改变之间,改变最右边的1的左边的位元(0变1,1变0)。比如G(4),0000,先改变最后一位0,得到0001,然后改变最右边1的左边的位元得0011;然后再次改变最后一位得0010,直到1000.简单实现~

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31void gray(int n)

{

int i;

char code[MAXN];

for(i=0; 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);

}

}

等等,写到这里还没有结束。还记得汉诺塔问题吗?一定要记得啊,因为我不想再复述了:D。

那么汉诺塔问题与格雷码有什么关系呢?我们知道汉诺塔问题要求一次移动一个圆盘,对应到格雷码,如果我们把最小的圆盘对应为格雷码的最后一位,我们会发现格雷码的改变位元正好对应相应的圆盘。还有个问题,就是第1块圆盘的移动有两种选择。不过不要紧,再稍加观察一下,我们就会发现第1块圆盘的移动是有规律的。假设圆盘最开始所在的柱子编号为1,最终都要移动到编号为2的柱子上,另外一个柱子编号为3。我们发现当盘子总数为奇数时,第1块圆盘的移动序列为123123…;总数为偶数时,移动序列为132132…。至于其它圆盘的移动,稍加判断就可以了。那么一个解汉诺塔问题的非递归算法就出来。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62void gray_hanoi(int n)

{

//3 pegs

stack *peg = new stack[3];

//1 disk moving order

vector order;

order.push_back(1);

for(int i=n; i>0; i--)

peg[0].push(i);

char code[N];

for(int i=0; i

code[i] = '0';

code[n] = '\0';

//针对n的奇偶性构造disk1的移动序列

if(n%2)

{

order.push_back(2);

order.push_back(3);

}

else{

order.push_back(3);

order.push_back(2);

}

int times = 0;

while(1)

{

if(code[n-1]=='0')

code[n-1] = '1';

else

code[n-1] = '0';

cout << "Move 1 from peg" << order[times%3] << " to peg" << order[(times+1)%3] << endl;

peg[order[times%3]-1].pop();

peg[order[(times+1)%3]-1].push(1);

times++;

int pos = n-1;

while(code[pos]=='0') pos--;

if(pos==0)

break;

pos--;

if(code[pos]=='0')

code[pos]='1';

else

code[pos]='0';

int from, to;

for(int i=0; i<3; i++)

{

if(!peg[i].empty() && peg[i].top()==n-pos)

from = i;

else if(peg[i].empty() || peg[i].top()>n-pos)

to = i;

}

peg[from].pop();

peg[to].push(n-pos);

cout << "Move " << n-pos << " from peg" << from+1 << " to peg" << to+1 << endl;

}

}

下面是当圆盘总数为4个时候的运行结果

无聊的同学可以手动验证一下:)

其实还有一个类似很有趣的问题:九连环问题。篇幅有限,这里就先不写了。大家回去自己看看吧!另外在集合的分割问题中,格雷码也有应用,等下次有空再写吧!

Happy New Year! Happy Weekend!

参考:《C语言名题精选百则》

wiki: 格雷码、汉诺塔、九连环, etc

c语言 格雷码构造问题,格雷码剖析相关推荐

  1. FPGA中有限状态机的状态编码采用格雷码还是独热码?

    今天看<从算法设计到硬件逻辑的实现>这本电子书时,遇到了一个问题,就是有限状态机的编写中,状态编码是采用格雷码还是独热码呢?究竟采用哪一种编码呢? 采用独热码为什么节省许多组合电路? 等等 ...

  2. BCD码、8421码、余三码、格雷码

    用四位二进制代码来表示一位十进制数,称为二--十进制编码,简称BCD(Binary Coded Decimal)码.根据代码的每一位是否有权值BCD码可分为有权码和无权码两类,应用最多的是8421BC ...

  3. php bcd编码,什么是BCD码、8421码、余三码、格雷码

    用四位二进制代码来表示一位十进制数,称为二--十进制编码,简称BCD(Binary Coded Decimal)码.根据代码的每一位是否有权值BCD码可分为有权码和无权码两类,应用最多的是8421BC ...

  4. 二进制码、格雷码、独热码的区别

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

  5. 【基础知识】~ 进制转换、补码、格雷码、BCD码、独热码

    1. 进制转换 1.1 十进制 to 二进制 正整数转二进制:除二取余,然后倒序排列,高位补零. 负整数转二进制:先是将对应的正整数转换成二进制后,对二进制取反,然后对结果再加一. 小数转二进制:对小 ...

  6. FPGA/数字IC之有限状态机:简介及其编码方式:顺序码、独热码与格雷码比较

    目录 一.状态机的分类 二.状态机的优点与存在的问题 三.状态的编码方式 四.三种方式的比较 一.状态机的分类 标准状态机分为摩尔(Moore)状态机与米勒(Mealy)状态机. 摩尔状态机的输出只与 ...

  7. Verilog常见编码方式:二进制码、格雷码、独热码

    Verilog常见编码方式有:二进制码.格雷码.独热码:三种编码方式各有特点利弊,因此有必要理解比较,以求不同场合的合理选择: 目录 一.结论: 二.详细了解 二进制码 格雷码 独热码 比较 三.参考 ...

  8. 自动发邮件的程序 c语言,5分钟!教你用C语言发送邮件:附送源码+教学!

    5分钟!教你用C语言发送邮件:附送源码+教学!-1.jpg (10.71 KB, 下载次数: 0) 2018-9-3 02:21 上传 关注<一碳科技>,获取更多知识! 前言 相信年夜家都 ...

  9. c语言程序设计教程赵乘,《C语言程序设计教程》例题源码.pdf

    <C语言程序设计教程>例题源码 练习一 [例1.1]打印 "常熟理工学院"的汉语拼音. 程序源码如下: voidmain() { printf("chang ...

  10. e语言通用进销存源码_Go 语言设计哲学之五:代码风格的唯一标准

    一. gofmt Go 语言设计的目标之一就是解决大型软件系统的大规模开发的问题,解决大型团队的开发问题,Go 核心团队给它起了一个名字叫:规模化(scale). gofmt 是伴随着 Go 语言诞生 ...

最新文章

  1. shell 练习题01:列出使用最多的命令是哪些并且将这些命令输入到chy1.txt中
  2. 华为V3.4-RIP命令总结
  3. 创业必经之路——Paul Graham创业曲线
  4. MORAN文本识别算法开源,刷新多个OCR数据集state-of-the-art
  5. HTML5 Canvas中处理图像和视频
  6. mysql mvcc实例讲解_轻松理解MYSQL MVCC 实现机制
  7. Unity开发《一起来捉妖》教程 | 1.陀螺仪控制相机
  8. enablefeignclients 注解_Spring Boot 中 @EnableXXX 注解的驱动逻辑
  9. XPath: A Syntax for Describing Needles and Haystacks(Chapter 3 of XSLT 2nd Edition)
  10. 判断一个点是否在矩形内部_矩形、圆形泄水管规格型号优势
  11. 一条命通关,这个AI算法玩超级马里奥操作秀翻天丨视频+开源代码
  12. Start with - Connect by - level
  13. 红队web打点信息收集
  14. BZOJ3505 [Cqoi2014]数三角形
  15. 台式计算机如何拆硬盘,台式机如何更换硬盘
  16. 百度地图 公交线路查询
  17. 使用Notepad格式化xml
  18. 怎样把pdf转换成excel转换器
  19. 音频噪声抑制(2):维纳(Wiener)滤波器篇
  20. YQP36预加水盘式成球机设计(论文+DWG图纸)

热门文章

  1. html批量打印保存到pdf,批量打印成PDF时不用每次点击保存位置的技巧
  2. 【分享】VMwareESXI详细黑群晖教程 DS36156
  3. 基于Eclipse+Java+Swing+Mysql实现旅游管理信息系统
  4. gbk utf-8 asccl url
  5. 浏览器支持H.265解码总结
  6. WQM软件使用说明书
  7. verilog语法基础
  8. 物联网安全行业调研报告 - 市场现状分析与发展前景预测
  9. jq-ui-multiselect插件的使用
  10. PS抠图方法[photoshop中文教程]