转载自:https://mp.weixin.qq.com/s/jIVa_mmqHiqSloqyDhsimQ
4、格雷码
在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code)下面看下格雷码和普通二进制码的区别
十进制数 二进制码 4位格雷码
0 0000 0000
1 0001 0001
2 0010 0011
3 0011 0010
4 0100 0110
5 0101 0111
6 0110 0101
7 0111 0100
8 1000 1100
9 1001 1101
10 1010 1111
11 1011 1110
12 1100 1010
13 1101 1011
14 1110 1001
15 1111 1000
1 gray(1,['_','X']).
2 gray(N,C) :-
3 N > 1,
4 N1 is N-1,
5 gray(N1,C1),
6 write('N1 = '),write(N1),nl,
7 reverse(C1,C2),
8 write('C1 = '),write(C1),nl,
9 write('C2 = '),write(C2),nl,
10 prepend('_',C1,C1P),
11 prepend('X',C2,C2P),
12 write('C1P = '),write(C1P),nl,
13 write('C2P = '),write(C2P),nl,
14 append(C1P,C2P,C).
15
注释行:
1. 当只有一位码时的列表,初始值
2.
3. 确保N是大于1的
4. 将N减1赋给N1
5. 先递归到最小,从只有1位码的情况开始返回
6. 调式用代码行
7. 将列表C1反转到列表C2
8. 调式用代码行
9. 调式用代码行
10. 处理列表中的元素
11. 处理列表中的元素
12. 调式用代码行
13. 调式用代码行
14. 将C1P和C2P列表拼接到列表C
15. 空行
16 prepend(_,[],[]) :- !.
17 prepend(X,[C|Cs],[CP|CPs]) :-
18 atom_concat(X,C,CP),
19 prepend(X,Cs,CPs).
20
注释行:
16. 自己来填
17. 。。。。
18. 将原子X拼接到原子C的前面结果到CP
19. 递归处理后续元素
20. 空行
请仔细体会有调试行指示输出的代码:
?- consult('p3_04').
true.
?- gray(3,C),write(C).
N1 = 1
C1 = [0,1]
C2 = [1,0]
C1P = [00,01]
C2P = [11,10]
N1 = 2
C1 = [00,01,11,10]
C2 = [10,11,01,00]
C1P = [000,001,011,010]
C2P = [110,111,101,100]
[000,001,011,010,110,111,101,100]
C = ['000', '001', '011', '010', '110', '111', '101', '100'] .
?-
谓词gray_c/2.用缓存技术做个示例
21 :- dynamic gray_c/2.
22 gray_c(1,['0','1']) :- !.
23 gray_c(N,C) :-
24 N > 1, N1 is N-1,
25 gray_c(N1,C1),
26 reverse(C1,C2),
27 prepend(‘0',C1,C1P),
28 prepend(‘1',C2,C2P),
29 append(C1P,C2P,C),
30 asserta((gray_c(N,C) :- !)).
31
% Try the following goal sequence and see what happens:
% ?- [p3_04].
% ?- listing(gray_c/2).
% ?- gray_c(5,C).
% ?- listing(gray_c/2).
注释行:
21. %设置为动态谓词(运行时可以动态增删)
22. %自己来填
23. %确保N大于1,然后N减1后赋值给N1
24.
25. %递归处理剩余位
26. %将C1反转后给到C2
27. %将字符’0’和原子C1拼接
28. %将字符’1’和原子C2拼接
29. %将C1P和C2P列表拼接到列表C
30. %将事实gray_c/2动态添加到当前事实数据库
31. 空行
实际做个查询来看看效果是怎样的:
?- consult('p3_04').
true.
?- listing(gray_c/2).
:- dynamic gray_c/2.
gray_c(1, ['0', '1']) :-
!.
gray_c(N, C) :-
N>1,
N1 is N+ -1,
gray_c(N1, C1),
reverse(C1, C2),
prepend('0', C1, C1P),
prepend('1', C2, C2P),
append(C1P, C2P, C),
asserta((gray_c(N, C):-!)).
true.
?- gray_c(5,C).
C = ['00000', '00001', '00011', '00010', '00110', '00111', '00101', '00100', '01100'|...].
?- listing(gray_c/2).
:- dynamic gray_c/2.
gray_c(5, ['00000', '00001', '00011', '00010', '00110', '00111', '00101', '00100', '01100', '01101', '01111', '01110', '01010', '01011', '01001', '01000', '11000', '11001', '11011', '11010', '11110', '11111', '11101', '11100', '10100', '10101', '10111', '10110', '10010', '10011', '10001', '10000']) :-
!.
gray_c(4, ['0000', '0001', '0011', '0010', '0110', '0111', '0101', '0100', '1100', '1101', '1111', '1110', '1010', '1011', '1001', '1000']) :-
!.
gray_c(3, ['000', '001', '011', '010', '110', '111', '101', '100']) :-
!.
gray_c(2, ['00', '01', '11', '10']) :-
!.
gray_c(1, ['0', '1']) :-
!.
gray_c(N, C) :-
N>1,
N1 is N+ -1,
gray_c(N1, C1),
reverse(C1, C2),
prepend('0', C1, C1P),
prepend('1', C2, C2P),
append(C1P, C2P, C),
asserta((gray_c(N, C):-!)).
true.
?-
构造格雷码还可以用另一种方式定义:
32 gray_alt(1,['0','1']).
33 gray_alt(N,C) :-
34 N > 1,
35 N1 is N-1,
36 gray_alt(N1,C1),
37 postpend(['0','1'],C1,C).
38
注释行:
32. 当只有一位码时的列表,初始值
33
34. 确保N大于1,
35. N减1后赋值给N1
36. 递归处理后续元素
37.
38.
38 postpend(_,[],[]).
40 postpend(P,[C|Cs],[C1P,C2P|CsP]) :-
41 P = [P1,P2],
42 atom_concat(C,P1,C1P),
43 atom_concat(C,P2,C2P),
44 reverse(P,PR),
45 postpend(PR,Cs,CsP).
46
注释行:
39. 终止条件
40.
41. P是列表[0,1]或[1,0]即[P1,P2]
42. 将字符P1接到原子C的后面,结果给到C1P,字符本身也是原子
43. 将字符P2接到原子C的后面,结果给到C2P
44. 反转列表P到PR
45. 用列表PR去递归调用处理后续的原子
46. 空行
再观察一下运行结果:
?-
| consult('p3_04').
true.
?- gray_alt(5,C),write(C).
[00000,00001,00011,00010,00110,00111,00101,00100,01100,01101,01111,01110,01010,01011,01001,01000,11000,11001,11011,11010,11110,11111,11101,11100,10100,10101,10111,10110,10010,10011,10001,10000]
C = ['00000', '00001', '00011', '00010', '00110', '00111', '00101', '00100', '01100'|...] .
?-