維特比算法的基礎能夠歸納爲下面三點(吳軍:數學之美):
一、若是機率最大的路徑通過籬笆網絡的某點,則從開始點到該點的子路徑也必定是從開始到該點路徑中機率最大的。
二、假定第i時刻有k個狀態,從開始到i時刻的k個狀態有k條最短路徑,而最終的最短路徑必然通過其中的一條。
三、根據上述性質,咱們在計算第i+1狀態的最短路徑時,只須要考慮從開始到當前的k個狀態值的最短路徑和當前狀態值到第i+1狀態值的最短java
籬笆網絡node
以詞網格爲例,如何從詞網格中找到最優結果算法
咱們只須要計算每一個箭頭的分值,而後取整個路徑分值最高的那條路徑就是最優的分詞結果網絡
如何計算從「南」到「江」的最優路徑優化
根據第三條性質:this
已這種方式計算「南」到「橋」全部路徑,就能夠取出最佳路徑了。code
hanlp中的實現對象
/** * 維特比算法獲取最優路徑 * @param wordNet 詞網格對象 第一個節點和最後一個節點都是 " " * @return Vertex爲節點對象,返回最優路徑的節點 */ private static List<Vertex> viterbi(WordNet wordNet) { // 避免生成對象,優化速度 LinkedList<Vertex> nodes[] = wordNet.getVertexes(); LinkedList<Vertex> vertexList = new LinkedList<Vertex>(); for (Vertex node : nodes[1]) { node.updateFrom(nodes[0].getFirst()); //先爲第一個["南", "南京", "南京市"]節點 賦值(節點的from字段和分值weight) } for (int i = 1; i < nodes.length - 1; ++i) { // 第一層循環遍歷全部豎線上的節點集合 ["南", "南京", "南京市"] LinkedList<Vertex> nodeArray = nodes[i]; if (nodeArray == null) continue; //節點集合若是爲空,多是非中文的"長度佔位",跳過 for (Vertex node : nodeArray) { // 第二層循環遍歷豎線上節點集合中的元素 if (node.from == null) continue; //若是節點沒有from說明不是路徑上的元素,不計分 for (Vertex to : nodes[i + node.realWord.length()]) { // 第三層循環拿到當前豎線上節點集合的下一個豎線節點集合 to.updateFrom(node); //分別賦值(節點的from字段和分值weight) } } } Vertex from = nodes[nodes.length - 1].getFirst(); // 獲取最後一個節點(" ") while (from != null) { // 通過updateFrom()方法 已經將每步的最優路徑賦值到from上了,因此在這裏直接從鏈表中取from就能夠取出整個最優路徑 vertexList.addFirst(from); from = from.from; } return vertexList; }
updateFrom()方法get
public void updateFrom(Vertex from) { double weight = from.weight + MathTools.calculateWeight(from, this); // 計算from節點到當前節點的分值 此值越低越優 if (this.from == null || this.weight > weight) { // 若是當前階段沒有from 或者 當前分值大於新的分值 則賦值 this.from = from; this.weight = weight; } }
維特比算法能夠計算全部相似於可用這種網格形式表示的最佳路徑,不單單用於分詞數學