在疯狂的考试周里,信息论老师美美地安排了课程设计并要求在考试前一天完成,于是乎,在这周我面临了三门考试+一个课程设计,这样的生活不要太充实…不过既然要做,那肯定要有所收获,不然我白通宵了5555
一、题目要求
课程目的:利用编程语言实现霍夫曼、费诺、香农编码
1、编码要求:
- 霍夫曼编码:实现任意Q符号的N重序列信源的最优R进制编码,这里:
8 ≤ Q ≤ 15 ; 2 ≤ R ≤ 5 ; 1 ≤ N ≤ 3 8≤Q≤15;2≤R≤5;1≤N≤3 8≤Q≤15;2≤R≤5;1≤N≤3 - 费诺、香农编码:实现任意Q符号信源的二进制编码,这里:
Q ≥ 10 Q≥10 Q≥10
二、pyQt5操作指南
非常简单!!
- 使用
pip install pyqt5
直接安装,安装好以后在Project Interpreter中可以看到安装好的包:
- 设置pyqt5 designer,这一步很关键,因为你可以直接像画流程图一样设计你的现实界面,而且可以直接到处相应的代码而不需要自己敲!
首先在Tools选项中选择Exrternal Tools,pyqt5 designer需要我们自己添加
Name自己随便设,在Program一栏中选择designer.exe路径,Woring directory填上$ProjectFileDir$
,用来指向当前文件所在路径。
以上都设置好了以后,就可以直接通过下图方式打开pyqt5 desginer。
pyqt5 designer的界面是这样的,每次想创建一个新的界面直接创建一个新的Main Window就可以了。
其他更有趣的操作大家还是亲手体验一下吧~
三、设计思路
我参考了其他大佬的思路,用pyQt5实现可视化操作界面(最难的也在这里,其实pyQt5很好实现的,如果时间充裕的话只要有耐心就能够搞定!!)
1、开发环境与工具
- 编辑工具:Pycharm 2020.1.5 x64
- 编译工具:Python 3.8
- 界面工具:PyQt5 Designer
2、一些代码参考
(1)霍夫曼编码
def Hoffman_Coding(self):
Q,N,R,sp = self.Read_Input()
if Q == 0 and N == 0 and R == 0 and sp == 0:
self.ui_error.show()
return 0
all_sym = self.Get_All_Symbol(Q,N,R,sp) # all nodes
if R == 2:
hoffman_tree = self.Hoffman_Tree_Generate_2(Q,N,sp,all_sym)
hoffman_code = self.Coding_2(all_sym,hoffman_tree)
outcome = ''
for i in range(len(all_sym)):
outcome = outcome + str(round(all_sym[i].P,6)) + ' ' + hoffman_code[i] + ' ' +'\n'
# print(outcome)
self.ui.textEdit_2.setPlainText(outcome)
self.Calculate_Performance_Index(N,R,sp,all_sym,hoffman_code)
elif R == 3:
hoffman_tree = self.Hoffman_Tree_Generate_3(Q,N,sp,all_sym)
hoffman_code = self.Coding_3(all_sym, hoffman_tree)
outcome = ''
for i in range(len(all_sym)):
outcome = outcome + str(round(all_sym[i].P, 6)) + ' ' + hoffman_code[i] + ' ' + '\n'
self.ui.textEdit_2.setPlainText(outcome)
self.Calculate_Performance_Index(N, R, sp, all_sym, hoffman_code)
elif R == 4:
hoffman_tree = self.Hoffman_Tree_Generate_4(Q,N,sp,all_sym)
hoffman_code = self.Coding_4(all_sym, hoffman_tree)
outcome = ''
for i in range(len(all_sym)):
outcome = outcome + str(round(all_sym[i].P, 6)) + ' ' + hoffman_code[i] + ' ' + '\n'
self.ui.textEdit_2.setPlainText(outcome)
self.Calculate_Performance_Index(N, R, sp, all_sym, hoffman_code)
elif R == 5:
hoffman_tree = self.Hoffman_Tree_Generate_5(Q,N,sp,all_sym)
hoffman_code = self.Coding_5(all_sym, hoffman_tree)
outcome = ''
for i in range(len(all_sym)):
outcome = outcome + str(round(all_sym[i].P, 6)) + ' ' + hoffman_code[i] + ' ' + '\n'
self.ui.textEdit_2.setPlainText(outcome)
self.Calculate_Performance_Index(N, R, sp, all_sym, hoffman_code)
def Hoffman_Tree_Generate_2(self,Q,N,sp,all_sym):
queue = all_sym[:]
while len(queue) > 1:
queue.sort(key=lambda item:item.P)
node_child1 = queue.pop(0)
node_child2 = queue.pop(0)
node_father = Node_2(node_child1.P + node_child2.P)
node_father.child1 = node_child1
node_father.child2 = node_child2
node_child1.father = node_father
node_child2.father = node_father
queue.append(node_father)
queue[0].father = None
return queue[0]
def Hoffman_Tree_Generate_3(self,Q,N,sp,all_sym):
queue = all_sym[:]
while len(queue) > 1:
queue.sort(key=lambda item:item.P)
node_child1 = queue.pop(0)
node_child2 = queue.pop(0)
node_child3 = queue.pop(0)
node_father = Node_3(node_child1.P + node_child2.P + node_child3.P)
node_father.child1 = node_child1
node_father.child2 = node_child2
node_father.child3 = node_child3
node_child1.father = node_father
node_child2.father = node_father
node_child3.father = node_father
queue.append(node_father)
queue[0].father = None
return queue[0]
def Hoffman_Tree_Generate_4(self,Q,N,sp,all_sym):
queue = all_sym[:]
while len(queue) > 1:
queue.sort(key=lambda item:item.P)
node_child1 = queue.pop(0)
node_child2 = queue.pop(0)
node_child3 = queue.pop(0)
node_child4 = queue.pop(0)
node_father = Node_4(node_child1.P + node_child2.P + node_child3.P + node_child4.P)
node_father.child1 = node_child1
node_father.child2 = node_child2
node_father.child3 = node_child3
node_father.child4 = node_child4
node_child1.father = node_father
node_child2.father = node_father
node_child3.father = node_father
node_child4.father = node_father
queue.append(node_father)
queue[0].father = None
return queue[0]
def Hoffman_Tree_Generate_5(self,Q,N,sp,all_sym):
queue = all_sym[:]
while len(queue) > 1:
queue.sort(key=lambda item:item.P)
node_child1 = queue.pop(0)
node_child2 = queue.pop(0)
node_child3 = queue.pop(0)
node_child4 = queue.pop(0)
node_child5 = queue.pop(0)
node_father = Node_5(node_child1.P + node_child2.P + node_child3.P + node_child4.P + node_child5.P)
node_father.child1 = node_child1
node_father.child2 = node_child2
node_father.child3 = node_child3
node_father.child4 = node_child4
node_father.child5 = node_child5
node_child1.father = node_father
node_child2.father = node_father
node_child3.father = node_father
node_child4.father = node_father
node_child5.father = node_father
queue.append(node_father)
queue[0].father = None
return queue[0]
def Coding_2(self,all_sym,root):
codes = ['']*len(all_sym)
for i in range(len(all_sym)):
node_tem = all_sym[i]
while node_tem != root:
if node_tem.check_num_of_child() == 1:
codes[i] = '0' + codes[i]
else:
codes[i] = '1' + codes[i]
node_tem = node_tem.father
print(codes)
return codes
def Coding_3(self, all_sym, root):
codes = ['']*len(all_sym)
for i in range(len(all_sym)):
node_tem = all_sym[i]
while node_tem != root:
if node_tem.check_num_of_child() == 1:
codes[i] = '0' + codes[i]
elif node_tem.check_num_of_child() == 2:
codes[i] = '1' + codes[i]
else:
codes[i] = '2' + codes[i]
node_tem = node_tem.father
print(codes)
return codes
def Coding_4(self, all_sym, root):
codes = ['']*len(all_sym)
for i in range(len(all_sym)):
node_tem = all_sym[i]
while node_tem != root:
if node_tem.check_num_of_child() == 1:
codes[i] = '0' + codes[i]
elif node_tem.check_num_of_child() == 2:
codes[i] = '1' + codes[i]
elif node_tem.check_num_of_child() == 3:
codes[i] = '2' + codes[i]
else:
codes[i] = '3' + codes[i]
node_tem = node_tem.father
print(codes)
return codes
def Coding_5(self, all_sym, root):
codes = ['']*len(all_sym)
for i in range(len(all_sym)):
node_tem = all_sym[i]
while node_tem != root:
if node_tem.check_num_of_child() == 1:
codes[i] = '0' + codes[i]
elif node_tem.check_num_of_child() == 2:
codes[i] = '1' + codes[i]
elif node_tem.check_num_of_child() == 3:
codes[i] = '2' + codes[i]
elif node_tem.check_num_of_child() == 4:
codes[i] = '3' + codes[i]
else:
codes[i] = '4' + codes[i]
node_tem = node_tem.father
print(codes)
return codes
def Get_All_Symbol(self,Q,N,R,sp):
all_sym = []
symbol_sequence = []
nodes = []
for i in range(N):
symbol_sequence.append(0)
for i in range(pow(Q,N)):
j = N-1
P = sp[symbol_sequence[0]]
for k in range(N):
if k != 0:
P *= sp[symbol_sequence[k]]
if R == 2:
all_sym.append(Node_2(P))
elif R == 3:
all_sym.append(Node_3(P))
elif R == 4:
all_sym.append(Node_4(P))
elif R == 5:
all_sym.append(Node_5(P))
while symbol_sequence[j] == Q-1:
symbol_sequence[j] = 0
j -= 1
if j == -1:
# print(len(all_sym))
if( int((Q - R)/(R - 1))!=(Q - R)/(R - 1) ):
Q_expand = int((Q - R)/(R - 1)+1)*(R - 1)+R
for i in range(Q_expand - Q):
if R == 2:
all_sym.append(Node_2(0))
elif R == 3:
all_sym.append(Node_3(0))
elif R == 4:
all_sym.append(Node_4(0))
elif R == 5:
all_sym.append(Node_5(0))
return all_sym
symbol_sequence[j] += 1
def Read_Input(self):
Q = int(self.ui.lineEdit_5.text()) # num of symbol
N = int(self.ui.lineEdit_6.text())# num of sequence
R = int(self.ui.lineEdit_7.text()) # num of radix
text = self.ui.textEdit.toPlainText()
sp = self.Get_Symbol_Probability(text) # symbol probability
if Q<8 or Q>15 or N<1 or N>3 or R<2 or R>5 or len(sp)!=Q or sum(sp) < 0.99999:
print('!!')
# self.ui_error.show()
# print(sum(sp))
return 0,0,0,0
else:
# print(Q,N,R,sp)
return Q,N,R,sp
def Str_Find_HH(self,str): # HH:huan hang '\n'
n = len(str)
pos = list()
for i in range(n):
if str[i] == '\n':
pos.append(i)
# print(pos)
return pos
def Get_Symbol_Probability(self,str):
n = len(str)
pos = self.Str_Find_HH(str)
sp = list()
n_pos = len(pos)
for i in range(n_pos):
if i == 0:
sp.append(float(str[0:pos[i]]))
else:
sp.append(float(str[pos[i - 1] + 1:pos[i]]))
# print(sp)
return sp
def Calculate_Performance_Index(self,N,R,sp,all_sym,codes):
HU = 0
l_avr = 0
for i in range(len(sp)):
HU += -sp[i]*math.log2(sp[i])
for i in range(len(all_sym)):
l_avr += all_sym[i].P*len(codes[i])
effi = HU*N/l_avr/math.log2(R)
self.ui.lineEdit_2.setText(str(round(HU,5)))
self.ui.lineEdit_3.setText(str(round(l_avr,2)))
self.ui.lineEdit_4.setText(str(round(effi*100,4))+'%')
(2)费诺编码
def Feno_Coding(self):
Q, sp = self.Read_Input()
if Q == 0 and sp == 0:
QMessageBox.warning(self, "Error", "Invalid input. Please check your input.")
return
sp_node = []
print(Q)
self.feno_code = [''] * len(sp)
sp.sort(reverse=True)
for i in range(len(sp)):
sp_node.append(Node(sp[i], i))
self.Coding(sp_node)
# print(self.feno_code)
outcome = ''
for i in range(len(sp)):
outcome = outcome + str(sp[i]) + ' ' + self.feno_code[i] + ' ' + '\n'
self.ui.textEdit_2.setPlainText(outcome)
self.Calculate_Performance_Index(sp, self.feno_code)
def Coding(self, sp_node):
if len(sp_node) == 1:
return
findPos = 0
diff_c = 1
sum1 = 0
sum2 = 0
left_flag = 0 # 0 means left bigger than right
for i in range(len(sp_node) - 1):
for j in range(i + 1):
sum1 += sp_node[j].P
for k in range(i + 1, len(sp_node)):
sum2 += sp_node[k].P
if abs(sum1 - sum2) < diff_c:
diff_c = abs(sum1 - sum2)
if sum1 < sum2:
left_flag = 1
else:
left_flag = 0
findPos = i
sum1 = 0
sum2 = 0
for i in range(len(sp_node)):
if left_flag:
if i <= findPos:
self.feno_code[sp_node[i].pos] += '1'
else:
self.feno_code[sp_node[i].pos] += '0'
else:
if i <= findPos:
self.feno_code[sp_node[i].pos] += '0'
else:
self.feno_code[sp_node[i].pos] += '1'
left = []
right = []
for i in range(findPos + 1):
left.append(sp_node[i])
for i in range(findPos + 1, len(sp_node)):
right.append(sp_node[i])
self.Coding(left)
self.Coding(right)
def Read_Input(self):
Q = int(self.ui.lineEdit.text()) # num of symbol
text = self.ui.textEdit.toPlainText()
sp = self.Get_Symbol_Probability(text) # symbol probability
if Q < 10 or len(sp) != Q or sum(sp) < 0.99999:
print('!!')
# self.ui_error.show()
# print(sum(sp))
return 0, 0, 0, 0
else:
# print(Q,N,R,sp)
return Q, sp
def Str_Find_HH(self, str): # HH:huan hang '\n'
n = len(str)
pos = list()
for i in range(n):
if str[i] == '\n':
pos.append(i)
#pos.append(i)
#print(pos)
return pos
def Get_Symbol_Probability(self, str):
n = len(str)
pos = self.Str_Find_HH(str)
sp = list()
n_pos = len(pos)
#print(n_pos)
for i in range(n_pos):
if i == 0:
sp.append(float(str[0:pos[i]]))
else:
sp.append(float(str[pos[i - 1] + 1:pos[i]]))
print(sp)
return sp
def Calculate_Performance_Index(self, sp, codes):
HU = 0
l_avr = 0
for i in range(len(sp)):
HU += -sp[i] * math.log2(sp[i])
for i in range(len(sp)):
l_avr += sp[i] * len(codes[i])
effi = HU / l_avr / math.log2(2)
print(effi)
self.ui.lineEdit_2.setText(str(round(HU, 5)))
self.ui.lineEdit_3.setText(str(round(l_avr, 2)))
self.ui.lineEdit_4.setText(str(round(effi * 100, 4)) + '%')
(3)香农编码
def Shannon_Coding(self):
Q, sp = self.Read_Input()
if Q == 0 and sp == 0:
self.ui_error.show()
return
shannon_code = [''] * len(sp)
sp.sort(reverse=True)
for i in range(len(sp)):
P_acc = 0
code = ''
l = int(-math.log2(sp[i]) + 1)
for j in range(i):
P_acc += sp[j]
while True:
P_acc *= 2
if P_acc >= 1:
code += '1'
else:
code += '0'
if len(code) == l:
break
P_acc -= int(P_acc)
if P_acc == 0:
if (len(code) < l):
code += '0' * (l - len(code))
break
shannon_code[i] = code[:l]
# print(shannon_code)
outcome = ''
for i in range(len(sp)):
outcome = outcome + str(sp[i]) + ' ' + shannon_code[i] + ' ' + '\n'
self.ui.textEdit_2.setPlainText(outcome)
self.Calculate_Performance_Index(sp, shannon_code)
def Read_Input(self):
Q = int(self.ui.lineEdit.text()) # num of symbol
text = self.ui.textEdit.toPlainText()
sp = self.Get_Symbol_Probability(text) # symbol probability
if Q < 10 or len(sp) != Q or sum(sp) < 0.99999:
# self.ui_error.show()
# print(sum(sp))
return 0, 0, 0, 0
else:
# print(Q,N,R,sp)
return Q, sp
def Str_Find_HH(self, str): # HH:huan hang '\n'
n = len(str)
pos = list()
for i in range(n):
if str[i] == '\n':
pos.append(i)
# print(pos)
return pos
def Get_Symbol_Probability(self, str):
n = len(str)
pos = self.Str_Find_HH(str)
sp = list()
n_pos = len(pos)
for i in range(n_pos):
if i == 0:
sp.append(float(str[0:pos[i]]))
else:
sp.append(float(str[pos[i - 1] + 1:pos[i]]))
# print(sp)
return sp
def Calculate_Performance_Index(self, sp, codes):
HU = 0
l_avr = 0
for i in range(len(sp)):
HU += -sp[i] * math.log2(sp[i])
for i in range(len(sp)):
l_avr += sp[i] * len(codes[i])
effi = HU / l_avr / math.log2(2)
self.ui.lineEdit_2.setText(str(round(HU, 5)))
self.ui.lineEdit_3.setText(str(round(l_avr, 2)))
self.ui.lineEdit_4.setText(str(round(effi * 100, 4)) + '%')
(4)界面显示程序
这个py文件就是pyqt5 designer中导出的代码
(5)具体操作
这里可能有点绕,拿霍夫曼编码界面举例,pyqt5 designer会自动生成一个ui文件,也就是这里的huffman.ui文件,然后用pyqt5 desginer导出一个py文件,即huffman_ui.py,它将操作可视化界面的具体设置放在了里面,比如设置了哪些元素(如放了一个文本框,放了一个标签,放了一个按钮,界面大小是多大等等),最后huffman.py会调用huffman_ui.py,在这个文件中,会实例化我们创建的Main Window,代码如下:
class Mywindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = Huffman_MainWindow()
self.ui.setupUi(self)
并对里面的元素做一些具体的操作,比如实现按钮点击跳转(self.ui.PushButton.clicked.connect
(相应的函数))、读取输入框中的内容(self.ui.lineEdit.text()
,要注意我设置的输入框是lineEdit格式,可能还有textEdit等等的格式,不同格式读取输入内容的方式不同)、将输出内容显示在界面对应的文本框中(self.ui.lineEdit.text(xx)
)在这里这个函数就是对应的霍夫曼编码,霍夫曼编码的相关代码也会放在这个文件中。
四、界面展示
根据自己的喜好去设计界面,最后跑通代码是一件很有成就感的事情哦,所以大家加油吧~