小白给小白详解维特比算法(一)

article/2025/10/3 5:39:32

小白给小白详解维特比算法(一)


  • 小白给小白详解维特比算法一
    • 篱笆网络Lattice的最短路径问题
      • 这个问题长什么样子
      • 这个问题难在哪里
      • 简化成这个模样你总能回答了吧
      • 下一步我们该干什么
    • 别倒立了我们再从头想一下这个问题
      • 我们是怎么走过来的
      • 来我们从A开始走
      • 这次我们需要算的次数大约是多少呢
    • We are almost there

初见HMM求解状态序列用到的维特比算法时,其实内心真的是崩溃的:数不尽的假设和公式,让人头昏脑涨的同时也击溃了自信心。但是仔细研究一下会发现其实问题蛮简单的,本文就致力于尝试用更通俗的方式解释一下维特比算法和它是如何运用在HMM求解状态序列中的,因为我也是刚看个差不多……所以如果有不对的地方请各位直接喷别留情!
(以下部分内容参考了吴军《数学之美》26.1,感谢吴军博士通俗易懂的讲解)

篱笆网络(Lattice)的最短路径问题

这个问题长什么样子?

尝试着回答一下这个问题(就算没办法回答也请先别关闭这个窗口!):
已知下图的篱笆网络,每个节点之间的数字表示相邻节点之间的距离,举个例子来说,如果我走 AB1C2D1E A → B 1 → C 2 → D 1 → E ,这个距离是 6+6+5+4=21 6 + 6 + 5 + 4 = 21 。那么如果让你从A走到E,最短路径是哪一条呢?

image_1c6ojbg349vlhlc1dqk1kc91cac58.png-56.9kB
图1

这个问题难在哪里?

好啦不用尝试啦!显然大家都知道,通过穷举的方法是很容易得到最短路径,可是问题就在于如果穷举的话,需要的加法次数不用算你也知道实在是太多啦(每条路径需要计算 4 4 次加法,一共3×3×3=27条路径共 108 108 次计算)!像这种没几层的篱笆网络也就罢了,如果每层13个节点,一共12层(然而这个规模对于标注问题来说也依然根本不算什么),可想而知那个线有多乱,如果仅仅穷举的话,这个计算量(大致是每条 12 12 次计算,一共 1312 13 12 条路径共大约 12×13122×1015 12 × 13 12 ≈ 2 × 10 15 次计算)怕是超级计算机也吃不消。

为了不直接给公式让大家关掉窗口,我们尝试着一点一点来解决这个问题

简化成这个模样,你总能回答了吧?

如下图,如果我想让你找到 AE A → E 的最短路径,就很简单了吧?
image_1c6ojgubs15t3a1dbs15il7as5l.png-16.2kB
图2

显然图2上只有三条路径,我们分别计算之后得到最短路径应该是 AD3E A → D 3 → E 这一条,路程是17。

这个时候请把这个问题和上一个问题做一个对比,同时从相反的方向考虑一下上一个问题:如果我最终想要到达E这个节点,其实无论如何都是要经过D这一层的,那么要是我知道从A到D的每一个节点的最短路径长度(在刚才的图上,我们事实上假设了他们分别是15、14和12),再加上从D的各节点到E的路程就得到了最终路径的长度。

当然我们还需要再多想一点:如果想要真的按照A到E的最短路径来走的话,我们其实不会选择 D1 D 1 D2 D 2 这两个节点,因为哪怕(以) AD2 A → D 2 (为例)路径更短(就算是10),但是加上 D2E D 2 → E 的距离之后就变得更长了(这时候也是18)。相应的我们也就明白这样一个道理:虽然我们最后没有选择 D1 D 1 D2 D 2 这两个节点,但我们是否真的就不需要得到A到他们的最短路径了呢?答案当然是否定的:从刚才的例子我们很容易理解,站在D层的角度来看的话,最终的长度是由“历史”(A到每一个D的长度)和“未来”(每一个D到E的长度)同时决定的,只有同时掌握了“历史”和“未来”,世界才能在我们手中(旁白:你说什么呢)!

当然刚才我们从A到D层的路径是假设出来的,事实上如果我们真的要解决最开始那个问题,我们需要求解这个问题:
image_1c6ojkffu1ngj5mj1fe21ogh1vc162.png-17kB
图3

我们就可以知道,最终的最短路径到底走的是哪个D。

下一步我们该干什么?

我们已经明白了,为了确定从哪个D到E才是最短的,我们就必须确定A到每一个D的最短路径。诶?这个问题是不是从哪里见过?

其实这就是所谓的“动态规划”的核心了:子问题几乎是完全一样的,我们只要一个一个解决了子问题(的子问题),最终的问题就迎刃而解了。

为了确定这个问题,我们就需要一个D一个D地去考虑(旁白:???),比如在考虑 D1 D 1 的时候不考虑 D2D3E D 2 、 D 3 、 E 等等。把问题简化成这个样子:
image_1c6oo3b798cgkokui21apm1fa36f.png-40kB
图4

这个图是不是和图1 非常相似?

然后根据图3的思想,我们把它简化成这个样子:
image_1c6oh39kn1sc6140crspdud9ma37.png-24.8kB
图5

这个图是不是和图3非常相似?

问题相应就变成了从A到每一个C的最短路径是多少?

解决了这个问题的话,我们就可以知道,最终如果走了比如 D1 D 1 ,前面路过的到底是哪个C

再进一步递推:为了确定到某一个C的最短路径,我们需要确定的就是这个问题:
image_1c6oha3dfjn91eo41juqpcdqe44e.png-20.1kB
图6

不不不!聪明的你(O__O”…)一定发现了,这根本算不得什么问题,因为其实它是这样的:
image_1c6ohb3ijleq1c229j889e1o2k4r.png-21.2kB
图7

因为我们已经推到最后一步了!从A到每一个B的路径事实上都是已知的,我们只需要把他们加在一起去比较大小就可以了!

别忘了我们的目标是什么:我们事实上是要确认如果最终路过了 C1 C 1 ,前面路过的是哪一个B。

从动态规划的角度考虑,假设我们最终的路径能路过 C1 C 1 而我们已经走到了 C1 C 1 ,那么可以肯定地说,我们来到 C1 C 1 的路径一定是所有来到 C1 C 1 路径里面最短的那一条(在这个图上看应该是 B3 B 3 )。因为如果我们没有走最短的那一条(比如我们错走成了 B1 B 1 ),那么我们只要用更短的那一条( B3 B 3 )去替换也一样可以走到 C1 C 1

别倒立了,我们再从头想一下这个问题!

我们刚才是从最终的E节点倒推考虑的这个问题,这一次我们从A真正的走一遍。

我们是怎么走过来的

我们在每一层 M M 都仅仅需要考虑这样一个问题:

为了到下一个层的某一个确定的节点Ni,我们到底应该从哪一个 Mi M i 出发呢?

一旦确定了从哪一个 Mi M i 出发,其他的 Mj M j 就不在我考虑范围内了。
就像我们刚才说的,只要我能找到去 C1 C 1 应该从 B3 B 3 走,我就不需要考虑其他的到 C1 C 1 的路径了。这样就大大的减少了路径总数。

来,我们从A开始走

  1. AB A → B
    这个没啥说的:6,7,5

    我们顺利走到了B层

  2. ABC A → B → C
    我们确定到每一个C的节点,应该路过哪一个B:
    image_1c6oo9fo41unc1mc46he3ssp1r7c.png-21.2kB

    • C1 C 1 :6+5=11, 7+4=11, 5+4=9,最终选择 AB3C1 A → B 3 → C 1 ,抛弃其他到 C1 C 1 的路径,长度9
    • C2 C 2 :12 ,10 ,11, 最终选择 AB2C2 A → B 2 → C 2 ,抛弃其他到 C2 C 2 的路径,长度10
    • C3 C 3 :15,14,11,最终选择 AB3C3 A → B 3 → C 3 ,抛弃其他到 C3 C 3 的路径,长度11

    我们顺利走到了C层,同时得到了到每一个C的最短路程

  3. A(B)CD A ( → B ) → C → D
    我们确定到每一个D的节点,应该路过哪一个C
    image_1c6oobrs5k8kjm81plbeskk8e7p.png-32.3kB

    • D_1:9+7=16,10+5=15,11+5=16,最终选择 AB2C2D1 A → B 2 → C 2 → D 1 ,抛弃其他到 D1 D 1 的路径,长度15
    • D_2:17,14,18,最终选择 AB2C2D2 A → B 2 → C 2 → D 2 ,抛弃其他到 D2 D 2 的路径,长度14
    • D_3:12,13,17,最终选择 AB3C1D3 A → B 3 → C 1 → D 3 ,抛弃其他到 D3 D 3 的路径,长度12

    我们顺利走到了D层,同时得到了到每一个D的最短路程

  4. A(BC)DE A ( → B → C ) → D → E
    我们确定每一个D到最终节点E的路程
    image_1c6oplpus1or519sjsdp18cn7kp8j.png-27.4kB
    显然最后应该选择 D3 D 3 ,完整路径为
    AB3C1D3E A → B 3 → C 1 → D 3 → E
    路程为17。抛弃其他所有路径。

这次我们需要算的次数大约是多少呢?

简单来说,从A到B有3条路径,每一条我们需要算到每一个C的最短距离,所以是 2()×3(AB)×3(BC) 2 ( 路 径 加 法 数 ) × 3 ( A → B ) × 3 ( B → C ) ,只要我们确定了每一个到C的最短距离剩下的事情就可以从C开始考虑了:每一个C需要确定到每一个D的最短距离,最终再加上D到E的距离就搞定了( 2()×3(C)×3(D) 2 ( 路 径 加 法 数 ) × 3 ( C 个 数 ) × 3 ( D 个 数 ) )。
如果换成是12层,每层最多13节点的话,每推一步的计算量最大规模在 132 13 2 (为了确定从哪一个B来到C,需要对每一个B到每一个C进行一次计算,上述计算每步是 32=9 3 2 = 9 次),而因为子问题都是一样的,所以增长是与网络长度成正比的:推进12次也仅仅乘以12而已。当然计算的方式不可能像这样简单乘一乘就搞定,但是可以看得出要比穷举要简单得多了。

We are almost there!

这几乎就是维特比算法了。至于维特比算法更公式化的描述和在隐含马尔科夫过程中的应用,我们下一文再说!


http://chatgpt.dhexx.cn/article/JY2oCFJ7.shtml

相关文章

viterbi算法

维特比算法(Viterbi Algorithm) 标签(空格分隔): 未分类 维特比算法(Viterbi Algorithm) viterbi算法用于寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states) 对于…

维特比算法

1. 概述 维特比算法是安德鲁.维特比(Andrew Viterbi)于1967年为解决通信领域中的解码问题而提出的,它同样广泛用于解决自然语言处理中的解码问题,隐马尔可夫模型的解码是其中典型的代表。无论是通信中的解码问题还是自然语言处理中的解码问题&#xff0c…

维特比算法(Viterbi algorithm)

一、维特比算法(Viterbi)原理以及简单实现 维特比算法(Viterbi algorithm)是一种动态规划算法。它用于寻找最有可能产生观测事件序列的维特比路径——隐含状态序列,特别是在马尔可夫信息源上下文和隐马尔可夫模型中。 维特比算法实际是用动…

机器学习:维特比算法(Viterbi Algorithm)

一、维特比算法(Viterbi Algorithm)讲解方式01:篱笆网络(Lattice)的最短路径问题 已知下图的篱笆网络,每个节点之间的数字表示相邻节点之间的距离,举个例子来说,如果我走&#xff0…

字符串匹配原理及实现(C++版)

字符串匹配原理及实现(C版) 1. 字符串匹配概念2. BF2.1 原理2.2 代码实现 3. KMP3.1 原理3.2 代码实现 4. BM4.1 坏字符4.2 好后缀4.3 代码实现 1. 字符串匹配概念 在查找操作中,我们用到很重要的概念就是字符串匹配,所谓字符串匹…

C++之单字符串匹配问题

有很多算法可以解决单模式匹配问题。而根据在文本中搜索模式串方式的不同,我们可以将单模式匹配算法分为以下几种: 基于前缀搜索方法:在搜索窗口内从前向后(沿着文本的正向)逐个读入文本字符,搜索窗口中文…

字符串——字符串匹配

文章目录 字符串匹配一、基本概念字符串匹配问题符号和术语后缀重叠引理 二、朴素字符串匹配算法三、Rabin-Karp算法(字符串Hash算法)进制Hash质数Hash 四、利用有限自动机进行字符串匹配有限自动机字符串匹配自动机计算状态转移函数代码 五、Knuth-Morris-Pratt算法模式的前缀…

朴素字符串匹配

描述 字符串匹配问题的形式定义: 文本(Text)是一个长度为 n 的字符串:T;模式(Pattern)是一个长度为 m 且 m≤n 的字符串:P; T 和 P 中的元素都属于有限的字母表 Σ 表; 有效位移 (Valid Shift): 如果 0≤ s ≤n-m,并且 T[s1…sm] P[1…m]&#xff0c…

算法之字符串匹配一

目录 前言: BF算法: RK算法 总结: 参考资料 前言: 字符串匹配指的是一个短点的字符串与一个长点的字符串进行匹配,并确认短的字符串是否在长的字符串中存在。匹配算法有很多,本文介绍两种简单、容易…

字符串匹配算法(C语言实现)

目录 文章目录 前言 一、BF算法 二、KMP算法 1.算法介绍 2.算法思路 3.整体代码实现 总结 前言 字符串匹配算法又称模式匹配算法,该算法的目的是为了子串从主串中寻找是否有与其匹配的部分, 其可分为BF暴力检索、RK哈希检索、KMP、BM等等,本…

shell字符串匹配

一、简介 Bash Shell提供了很多字符串和文件处理的命令。如awk、expr、grep、sed等命令,还有文件的排序、合并和分割等一系列的操作命令。grep、sed和awk内容比较多故单独列出,本文只涉及字符串的处理和部分文本处理命令。 二、字符串处理 1、expr命令…

golang字符串匹配算法

简介 字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为模式串)。在 Golang 中,可以使用最常见的字符串匹配算法之一:Knuth-Morris-Pratt(KMP)算法,它的时间复杂度为 O(nm…

【数据结构】字符串匹配(暴力匹配)

原理解析: 字符串匹配方法,创建两个字符串结构,主 串和子串比较。 主串字符 a 和 子串字符 c 不匹配,主串的指针向下移动,移动到上一次开始比较的下一个位置。 子串指向开始的位置。 主串字符 b 和 子串字符 c 不匹配…

字符串匹配算法比较

字符串匹配(string match)是在实际工程中经常会碰到的问题,通常其输入是原字符串(String)和子串(又称模式,Pattern)组成,输出为子串在原字符串中的首次出现的位置。通常精确的字符串搜索算法包括暴力搜索(Brute force)…

子串查找(字符串匹配)

子串查询 首先,我们来定义两个概念,主串和模式串。我们在字符串 A 中查找字符串 B,则 A 就是主串,B 就是模式串。我们把主串的长度记为 n,模式串长度记为 m。由于是在主串中查找模式串,因此,主串…

字符串匹配算法综述

字符串匹配算法综述 字符串匹配算法综述:BF、RK、KMP、BM、Sunday 字符串匹配算法,是在实际工程中经常遇到的问题,也是各大公司笔试面试的常考题目。此算法通常输入为原字符串(string)和子串(pattern&…

字符串匹配算法详解

希望看到文章的你们,能够在今年的研究生考试中超常发挥。 愿你们都能考上自己心仪的学校,为你们的备考生涯划上一个完美的句号。做为你们的师兄有几句话想对你们说,希望这些话能对你们有一些帮助。 马上就要考试了,不要再继续啃难…

字符串匹配算法

字符串匹配就是在主串A中查找模式串B,例如在主串abababc中查找模式串abc是否存在,记主串A的长度为n,模式串B的长度为m,n>m。 BF算法 BF(Brute Force)算法,又叫暴力匹配算法或者朴素匹配算法,思路很简单…

字符串(字符串匹配)

一、字符串匹配问题、基础 1、假设文本是一个长度为n的数组T,而模式是长度为m的数组P,我们希望在文本T中寻找模式P 如果P出现在T中的第s个位置,那么我们称其有效偏移为s,在其他不匹配的位置称为无效偏移 2、如果字符串w是字符串…

字符串匹配

字符串匹配 1.朴素的串匹配算法(暴力解法) 1.1 分析 设t是目标串(母串),p是模式串(待匹配串),i , j 分别指向 模式串 和 目标串,m、n分别是模式串p和目标串t的长度。 从目标串的第0个字符&am…