お元気ですか
最適化問題を解決するためのアルゴリズムは、通常、一連のステップを経る必要があり、各ステップは複数の選択肢に直面します。多くの最適化問題では、動的計画法アルゴリズムを使用して最適な解を見つけるのは少し注意が必要であり、より単純で効率的なアルゴリズムを使用できます。欲張りアルゴリズムはそのようなアルゴリズムであり、各ステップで最も見栄えの良い選択を行います。つまり、常に局所的に最適な選択を行います。
貪欲な選択の性質
ローカル最適(貪欲)選択を行うことでグローバル最適解を構築できます。選択を行うときは、サブ問題の解を考慮することなく、現在の問題で直接最適選択を行います。貪欲アルゴリズムは通常、トップダウンで、特定の問題インスタンスを小さくするために何度も選択を行います。
ハフマン符号化
ハフマン符号化は、データを非常に効果的に圧縮できます。100,000文字のデータファイルを圧縮するとします。次の図は、ファイル内の文字とその頻度を示しています。つまり、ファイル内には6つの異なる文字しかなく、その中に文字aaがあります。は45,000回登場しました。
固定長エンコーディングを使用して3ビットコードワードを指定すると、ファイルを300,000ビットの長さにエンコードできますが、上の表に示す可変長エンコーディングを使用すると、ファイルのエンコードに使用されるのは224,000ビットのみです。(45 ∗ 1 + 13 ∗ 3 + 12 ∗ 3 + 16 ∗ 3 + 9 ∗ 4 + 5 ∗ 4)∗ 1000 = 224000(45 * 1 + 13 * 3 + 12 * 3 + 16 * 3 + 9 * 4 + 5 * 4)* 1000 = 224000(4 5∗1+1 3∗3+1 2∗3+1 6∗3+9∗4+5∗4 )∗1 0 0 0=2 2 4 0 0 0に対応する二分木は次のように表され、各リーフノードは文字とその出現頻度をマークします。各内部ノードは、そのサブツリー内のリーフノードの頻度の合計でマークされます。(a)固定長コーディングに対応a = 000、...、f = 101 a = 000、...、f = 101a=0 0 0 、。。。、f=1 0 1、(b)の二分木は、最適なプレフィックスコードa = 0、b = 101、...、F = 1100 a = 0、b = 101、...、f = 1100に対応します。a=0 、b=1 0 1 、。。。、f=1 1 0 0バイナリツリー。
qian'zhuiプレフィックスコードに対応するツリーTTが与えられますT、アルファベットCCの場合、ファイルをエンコードするために必要なバイナリビット数を簡単に計算できます。内のすべての文字CのCCc、属性c。freqc.freqを許可します。C 。FのR E Qの手段CCcの頻度がファイルに表示されます。dT(c)d_T(c)とします。dT(c )はccを意味しますツリー内のcの葉ノードの深さ。次に、エンコードされたファイルにはB(T)= ∑c∈Ccが必要です。周波数DT(c)B(T)= \ sum_(c \ in C)c.freq.d_T(c)B (T )=C ∈ CΣc 。FのR EのQ 。dT(c )バイナリビット、B(T)B(T)B (T )はTTとして定義されますTの価格。
ハフマンコードの構築
ハフマンは、ハフマンコードと呼ばれる最適なプレフィックスコードを構築するための欲張りアルゴリズムを設計しました。CCを想定Cはnnですn文字のセット、および各文字c∈Cc\ in Cc∈Cは、属性cを持つオブジェクトです。freqc.freqC 。FのR EのQは、文字の発生頻度を与えます。アルゴリズムは、対応する最適なコーディング二分木TTをボトムアップで構築します。T。∣ C ∣ | C |から始まります| C |リーフ・ノードで開始し、実行| C | - 1 | C | -1∣ C ∣−1回の「マージ」操作により、最終的なバイナリツリーが作成されます。次の図に示すように、ハフマンアルゴリズムの実行プロセス:
Pythonは次のようにハフマンコーディングを実装します。
# -*-coding:utf8 -*-
import sys
#构造节点
class Node(object):
def __init__(self, key, code=0, parent=None,lchild=None, rchild=None):
self.key = key
self.code = code
self.parent = parent
self.lchild = lchild
self.rchild = rchild
class HuffmanTree(object):
def __init__(self,root=None):
self.huffman_tree = []
self.root = root
#找两个key值最小的节点
def find2node(self, flag):
mini = []
for i in range(len(self.huffman_tree)):
if i in flag:
continue
if len(mini) < 2:
mini.append(i)
else:
if self.huffman_tree[i].key < max([self.huffman_tree[mini[0]].key, self.huffman_tree[mini[1]].key]):
pos = 0 if self.huffman_tree[mini[0]].key > self.huffman_tree[mini[1]].key else 1
mini[pos] = i
# let lchird.key < rchild.key
if self.huffman_tree[mini[0]].key > self.huffman_tree[mini[1]].key:
temp = mini[0]
mini[0] = mini[1]
mini[1] = temp
#return sorted(mini)
return mini
def build(self, C):
#flag记录已经合并的节点
flag = []
#初始化各关键字key节点
n = len(C)
for key in C:
self.huffman_tree.append(Node(key))
#构建hufuman,n个值,需要n-1次合并操作
for i in range(n,2*n-1):
mini = self.find2node(flag)
print(str(mini[0])+'\t'+str(mini[1]))
#合并的节点记录,下次合并不需要考虑
flag.append(mini[0])
flag.append(mini[1])
#构建新的Node节点,key为两个子节点key相加
key = self.huffman_tree[mini[0]].key + self.huffman_tree[mini[1]].key
node = Node(key,lchild=self.huffman_tree[mini[0]],rchild=self.huffman_tree[mini[1]])
#新节点加入
self.huffman_tree.append(node)
self.huffman_tree[mini[0]].parent = node
self.huffman_tree[mini[1]].parent = node
self.huffman_tree[mini[1]].code = 1
#记录root节点
print(node.key)
self.root = node
#中序遍历
def inorder_tree_walk(self, tree):
if tree is not None:
self.inorder_tree_walk(tree.lchild)
print(tree.key, end=" ")
self.inorder_tree_walk(tree.rchild)
#获取节点的编码
def huffman_code(self, cur):
path = []
while cur.parent!=None:
#path结果保存从根节点搜索路径的编码结果
path.insert(0,cur.code)
cur = cur.parent
return path
if __name__=='__main__':
tree = HuffmanTree()
C = [5,9,12,13,16,45]
tree.build(C)
tree.inorder_tree_walk(tree.root)
print()
#打印每个叶子节点的赫夫曼编码
for i in range(len(C)):
path = tree.huffman_code(tree.huffman_tree[i])
print('key='+str(tree.huffman_tree[i].key))
print(path)
特定のコードについては、各章のアルゴリズムpython実装のgithubアドレスアルゴリズムの概要を参照してください。