转载自:https://mp.weixin.qq.com/s/JkjTL5yGhNQaww6GMiwX1A
5、哈夫曼码
首先,研究一本有关离散数学或算法的好书,以详细描述霍夫曼代码,或者查阅 维基百科。
我们假设一组符号及其频率作为fr(S,F)项的列表给出。
例如: [fr(a,45),fr(b,13),fr(c,12),fr(d,16),fr(e,9),fr(f,5)].
我们的目标是构造一个列表hc(S,C)项,其中C是符号S的霍夫曼代码字。在我们的示例中,结果可能是[hc(a, '0'), hc(b, '101'), hc(c, '100'), hc(d, '111'), hc(e, '1101'), hc(f, '1100')]
该任务应由以下定义的谓词huffman/2执行:
huffman(Fs,Hs):- Hs是频率表Fs (list-of-fr/2-terms, list-of-hc/2-terms)的霍夫曼代码表。
在构造过程中,我们需要节点n(F,S),其中开始时F是频率,S是符号。在此过程中,随着n(F,S)成为内部节点,S成为项s(L,R),其中L和R再次成为n(F,S)项。称为Ns的n(F,S)项列表被维护为一种优先级队列。
1 huffman(Fs,Cs) :-
2 initialize(Fs,Ns),
3 make_tree(Ns,T),
4 traverse_tree(T,Cs).
5
注释行:
1.
2. 初始化
3. 生成树
4. 遍历树T并构造霍夫曼代码表Cs
5. 空行
6 initialize(Fs,Ns) :-
7 init(Fs,NsU),
8 sort(NsU,Ns).
9
注释行:
6.
7. %根据列表Fs初始化列表项NsU
8. %对列表项NsU排序,结果放到Ns
9.
10 init([],[]).
11 init([fr(S,F)|Fs],[n(F,S)|Ns]) :-
12 init(Fs,Ns).
13
注释行:
10. %终止条件
11. %根据列表Fs内的元素项重新名命函子名为n的项的列表
12. %递归遍历处理后续列表项
13. 空行
测试:
?- init([fr(a,45),fr(b,13),fr(c,12),fr(d,16),fr(e,9),fr(f,5)],X),sort(X,Y).
X = [n(45, a), n(13, b), n(12, c), n(16, d), n(9, e), n(5, f)],
Y = [n(5, f), n(9, e), n(12, c), n(13, b), n(16, d), n(45, a)].
14 make_tree([T],T).
15 make_tree([n(F1,X1),n(F2,X2)|Ns],T) :-
16 F is F1+F2,
17 insert(n(F,s(n(F1,X1),n(F2,X2))),Ns,NsR),
18 write('Ns = '),write(Ns),nl,
19 write('NsR = '),write(NsR),nl,
20 make_tree(NsR,T).
21
注释行:
14. 参数1列表中的项T,与参数2相同则终止
15.
16. 取列表的前两项的参数F1与F2求和后的值赋给F
17. 将节点n(F,s(n(F1,X1),n(F2,X2)))加入到Ns
18. 运行时观察点
19. 运行时观察点
20. 递归继续生成树
21.
我们来观察make_tree运行的过程
?- init([fr(a,45),fr(b,13),fr(c,12),fr(d,16),fr(e,9),fr(f,5)],X),sort(X,Y),write('Y = '),write(Y),nl,make_tree(Y,Z).
Y = [n(5,f),n(9,e),n(12,c),n(13,b),n(16,d),n(45,a)]
Ns = [n(12,c),n(13,b),n(16,d),n(45,a)]
NsR = [n(12,c),n(13,b),n(14,s(n(5,f),n(9,e))),n(16,d),n(45,a)]
Ns = [n(14,s(n(5,f),n(9,e))),n(16,d),n(45,a)]
NsR = [n(14,s(n(5,f),n(9,e))),n(16,d),n(25,s(n(12,c),n(13,b))),n(45,a)]
Ns = [n(25,s(n(12,c),n(13,b))),n(45,a)]
NsR = [n(25,s(n(12,c),n(13,b))),n(30,s(n(14,s(n(5,f),n(9,e))),n(16,d))),n(45,a)]
Ns = [n(45,a)]
NsR = [n(45,a),n(55,s(n(25,s(n(12,c),n(13,b))),n(30,s(n(14,s(n(5,f),n(9,e))),n(16,d)))))]
Ns = []
NsR = [n(100,s(n(45,a),n(55,s(n(25,s(n(12,c),n(13,b))),n(30,s(n(14,s(n(5,f),n(9,e))),n(16,d)))))))]
X = [n(45, a), n(13, b), n(12, c), n(16, d), n(9, e), n(5, f)],
Y = [n(5, f), n(9, e), n(12, c), n(13, b), n(16, d), n(45, a)],
Z = n(100, s(n(45, a), n(55, s(n(25, s(n(12, c), n(13, b))), n(30, s(n(14, s(n(5, f), n(9, e))), n(16, d))))))) .
22 insert(N,[],[N]) :- !. %
23 insert(n(F,X),[n(F0,Y)|Ns],[n(F,X),n(F0,Y)|Ns]) :-
24 F < F0, !.
25 insert(n(F,X),[n(F0,Y)|Ns],[n(F0,Y)|Ns1]) :-
26 F >= F0,
27 insert(n(F,X),Ns,Ns1).
28
注释行:
22. 当参数项1是参数3的列表项则截断终止
23. 将节点n(F,X)插入到Ns中,以使结果列表NsR再次相对于频率F进行排序。
24. 当F小于F0此节点的插入作为终结点
25.
26. 当F大于等于F0
27. 继续递归插入后续节点
28.
29 traverse_tree(T,Cs) :-
30 traverse_tree(T,'',Cs1-[]),
31 sort(Cs1,Cs).
32
33 traverse_tree(n(_,A),Code,[hc(A,Code)|Cs]-Cs) :-
34 atom(A).
35 traverse_tree(n(_,s(Left,Right)),Code,Cs1-Cs3) :-
36 atom_concat(Code,'0',CodeLeft),
37 atom_concat(Code,'1',CodeRight),
38 traverse_tree(Left,CodeLeft,Cs1-Cs2),
39 traverse_tree(Right,CodeRight,Cs2-Cs3).
40
注释行:
29. 遍历树T并构造霍夫曼代码表Cs traverse_tree/2
30. 根据T生成Cs1-[],Cs1是列表
31. 将列表Cs1排序后放入Cs作为结果
32. 空行
33. traverse_tree/3
34. 如果A是原子(叶)则此分支终止
35. 内部节点
36. 将字符’0‘拼接到Code原子的后面结果放入CodeLeft
37. 将字符’1‘拼接到Code原子的后面结果放入CodeRight
38. 递归处理左树
39. 递归处理右树
40. 空行
41 huffman(Fs) :-
42 huffman(Fs,Hs) ,
43 nl,
44 report(Hs,5),
45 stats(Fs,Hs),
46 write('Hs = '),write(Hs),nl.
47
注释行:
41. 输出统计信息
42. 生成哈夫曼码
43.
44. 输出报表
45. 统计
46. 调试观察点
47.
48 report([],_) :-
49 !,
50 nl,
51 nl.
52 report(Hs,0) :-
53 !,
54 nl,
55 report(Hs,5).
56 report([hc(S,C)|Hs],N) :-
57 N > 0, N1 is N-1,
58 writef('%w %w ',[S,C]),
60 report(Hs,N1).
61
注释行:
48. 报表为空截断后输出两行换行
49.
50.
51.
52. 取值0,5的问题留给读者
53.
54.
55.
56.
57. 确保N大于0 ,并且N减以后赋值给N1
58. 打印输出
59.
60. 递归输出
61.
62 stats(Fs,Cs) :-
63 sort(Fs,FsS),
64 sort(Cs,CsS),
65 stats(FsS,CsS,0,0).
66
67 stats([],[],FreqCodeSum,FreqSum) :-
68 Avg is FreqCodeSum/FreqSum,
69 writef('Average code length (weighted) = %w\n',[Avg]).
70 stats([fr(S,F)|Fs],[hc(S,C)|Hs],FCS,FS) :-
71 atom_chars(C,CharList),
72 length(CharList,N),
72 FCS1 is FCS + F*N,
73 FS1 is FS + F,
74 stats(Fs,Hs,FCS1,FS1).
75
注释行:
62.
63. %排序列表Fs,放到列表FsS中
64. %排序列表Cs,放到列表CsS中
65.
66.
67.
68.
69. %输出平均值
70.
71. %将字符串S装换为字符列表
72. %得到字符列表长度放到N
72. %初始值从0做递归累计
73. %初始值从0做递归累计
74. %递归累计
运行结果:
?- X = [fr(a,45),fr(b,13),fr(c,12),fr(d,16),fr(e,9),fr(f,5)], huffman(X).
a 0 b 101 c 100 d 111 e 1101
f 1100
Average code length (weighted) = 2.24
Hs = [hc(a,0),hc(b,101),hc(c,100),hc(d,111),hc(e,1101),hc(f,1100)]
X = [fr(a, 45), fr(b, 13), fr(c, 12), fr(d, 16), fr(e, 9), fr(f, 5)] .
?-