viterbi算法:利用动态规划寻找最短路径

动态规划是运筹学的一个分支,是求解决策过程最优化的数学方法,通常情况下应用于最优化问题,这类问题一般有很多个可行的解,每个解有一个值,而我们希望从中找到最优的答案。
在计算机科学领域,应用动态规划的思想解决的最基本的一个问题就是:寻找有向无环图(篱笆网络)当中两个点之间的最短路径(实际应用于地图导航、语音识别、分词、机器翻译等等)。

下面举一个比较简单的例子做说明:求S到E的最短路径。如下图(各点之间距离不相同):

我们知道,要找到S到E之间最短路径,最容易想到的方法就是穷举法。也就是把所有可能的路径都例举出来。从S走向A层共有4种走法,从A层走向B层又有4种走法,从B层走向C层又有4种走法,然后C层走向E点只有一种选择。所以最终我们穷举出了4X4X4=64种可能。显然,这种方法必定可行。但在实际的应用当中,对于数量极其庞大的结点数和边数的图,其计算复杂度也将会变得非常大,而计算效率也会随之降低。


因此,这里选择使用一种基于动态规划的方式来寻找最佳路径。
所谓动态规划。其核心就是“动态”的概念,把大的问题细分为多个小的问题,基于每一步的结果再去寻找下一步的策略,通过每一步走过之后的局部最优去寻找全局最优。这样解释比较抽象,下面直接用回刚刚的例子说明。如下图:

首先,我们假设S到E之间存在一条最短路径(红色),且这条路径经过C2点,那么我们便一定能够确定从S到C2的64条(4X4X4=64)子路径当中,该子路径一定最短。(证明:反证法。如果S到C2之间存在一条更短的子路径,那么便可以用它来代替原先的路径,而原先的路径显然就不是最短了,这与原假设自相矛盾)。
同理,我们也可以得出从S到B2点为两点间最短子路径的结论。这时候,真相便慢慢浮出水面:既然如此,我们计算从S点出发到点C2的最短路径,是不是只要考虑从S出发到B层所有节点的最短路径就可以了?答案是肯定的!因为,从S到E的“全局最短”路径必定经过在这些“局部最短”子路径。没错!这就是上面提及到的通过局部最优的最优去寻找全局最优,问题的规模被不断缩小!

接下来,要揭晓答案了!继续看下图:

回顾之前的分析:我们计算从S起到C2点的最短路径时候只需要考虑从S出发到B层所有节点的最短路径,B层也如是。对B2来说,一共有4条路线可以到达,分别是A1→B2,A2→B2,A3→B2,A4→B2。我们需要做的就是把A2→B2这条最短路线保留,而其他3条删除掉(因为根据以上的分析,它们不可能构成全程的最短路线)。OK,来到这里,我们会发现一个小“漏洞”,这段S→A2→B2→C2→E的路线只是我一厢情愿的假设,最短路径不一定是经过以上这些点。所以,我们要把每层的每个节点都考虑进来。

以下是具体的做法:
step1:从点S出发。对于第一层的3个节点,算出它们的距离d(S,A1),d(S,A2),d(S,A3),d(S,A4),因为只有一步,所以这些距离都是S到它们各自的最短距离。

step2:对于B层的所有节点(B1,B2,B3,B4),要计算出S到它们的最短距离。我们知道,对于特定的节点B2,从S到它的路径可以经过A层的任何一个节点(A1,A2,A3,A4)。对应的路径长就是d(S,B2)=d(S,Ai)+d(Ai,B2)(其中i=1,2,3,4)。由于A层有4个节点(即i有4个取值),我们要一一计算,然后找到最小值。这样,对于B层的每个节点,都需要进行4次运算,而B层有4个节点,所以共有4X4=16次运算。

step3:这一步是该算法的核心。我们从step2计算得出的结果只保留4个最短路径值(每个节点保留一个)。那么,若从B层走向C层来说,该步骤的基数已经不再是4X4,而是变成了4!也就是说,从B层到C层的最短路径只需要基于B层得出的4个结果来计算。这种方法一直持续到最后一个状态,每一步计算的复杂度为相邻两层的计算复杂度为4X4乘积的正比!再通俗点说,连接这两两相邻层的计算符合变成了“+”号,取代了原先的“X”号。用这种方法,只需进行4X4X2=32次计算!

其实上述的算法就是著名的维特比算法,事实上非常简单!
若假设整个网格的宽度为D,网格长度为N,那么若使用穷举法整个最短路径的算法复杂度为O(DN),而使用这种算法的计算复杂度为O(ND2)。试想一下,若D与N都非常大,使用维特比算法的效率将会提高几个数量级!

代码实现(C语言版):

同样是实现从S到E的最短路径。不过这次把刚刚的情况简化了一下,原理是相同的。

#include<stdlib.h> 
#include<stdio.h>  
#define x 9999
#define max 9999
int data[10][10];
int dist[10];//记录最短路径为多少
int path[10];//记录最短路径
int kmin(int,int);
void fpath(int a[][10]);
int froute(int a[][10]);
void main()
{
    int i,m;
    int a[10][10]=
  {
    {x,4,2,3,x,x,x,x,x,x}, 
    {x,x,x,x,10,9,x,x,x,x},
    {x,x,x,x,6,7,10,x,x,x},
    {x,x,x,x,x,3,8,x,x,x},
    {x,x,x,x,x,x,x,4,8,x},
    {x,x,x,x,x,x,x,9,6,x},
    {x,x,x,x,x,x,x,5,4,x},
    {x,x,x,x,x,x,x,x,x,8},
    {x,x,x,x,x,x,x,x,x,4},
    {x,x,x,x,x,x,x,x,x,x}
  };
    /*for (i=0;i<10;i++)
    {
    for(j=0;j<10;j++)
    printf("%d ",a[i][j]);
    printf("\n"); 
    }*/
    fpath(a);
      printf("最短路径大小为: %d\n",dist[9]);

    m=froute(a);
    for(i=m-1;i>=0;i--)
      printf("最短路径经过: %d\n",path[i]);
    }
    void fpath(int a[][10])
    {
      int i,j,k;42 dist[0]=0;
      for(i=1;i<10;i++)
      {
        k=max;
        for(j=0;j<i;j++)
        {
          if(a[j][i]!=x)
            if((dist[j]+a[j][i])<k)
            k=dist[j]+a[j][i];  
        } 
      dist[i]=k;
      }
    }
    int froute(int a[][10])
    {
     int j,b,k=1,i=9;
     path[0]=10;
     while(i>0)
     {
      for(j=i-1;j>=0;j--)
      { 
       if(a[j][i]!=x)
       {
        b=dist[i]-a[j][i];
        if(b==dist[j])
        {
          path[k++]=j+1;
          i=j; 
          break;
        }
      }
    } 
  }
  return k;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容