序列比对(十)——viterbi算法求解最可能路径

本文探讨了如何使用Viterbi算法来找到概率最高的状态序列,即最可能的路径。通过掷骰子的HMM模型,解释了算法如何解决后一状态依赖前一状态的问题,并给出了具体的C语言代码实现,强调了处理概率相乘可能导致的下溢问题,通过取log值避免错误。
摘要由CSDN通过智能技术生成

原创: hxj7

本文介绍了如何使用viterbi算法得到概率最大的路径。

前文《序列比对(九)从掷骰子说起HMM》已经通过投骰子的例子介绍了HMM的基本概念,引用如下:
在这里插入图片描述
那么如何通过已知的符号序列来预测未知的状态序列呢?我们将一串状态序列称为一条路径,那么如果要选择其中的一条路径作为预测结果, 也许我们该选择概率最大的,具体如下:
在这里插入图片描述
图片引自《生物序列分析》

其实,正如《序列比对(八)第一部分的小结》所说,后一状态依赖于前一个状态的问题很适合用动态规划算法解决。viterbi算法就是一种基于动态规划的求解最可能路径的算法。

更具体地,还以前文提到的掷骰子为例,当根据初始向量、转移矩阵、发射矩阵等参数生成一个随机的符号序列后,我们可以利用viterbi算法来求解最可能的路径。简单来讲,就是用viterbi算法来猜每次投掷用的是公平骰子还是作弊骰子。(如果对投骰子的例子不熟悉,请参考前文《序列比对(九)从掷骰子说起HMM》)

效果如下:在这里插入图片描述
上图中Rolls代表300次投掷所产生的符号序列,Die表示投掷时实际所使用的骰子状态(F表示公平骰子,L表示作弊骰子),Viterbi表示利用viterbi算法求解的最可能路径。

具体代码如下:

(需要说明的是,实现viterbi算法要特别注意多个概率相乘会得到一个特别小的数,容易造成下溢,从而出错。所以我们将概率取log值,将公式中的概率相乘变成了log值的相加。)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#define MIN_LOG_VALUE -99

typedef char State;
typedef char Result;
State state[] = {
   'F', 'L'};   // 所有的可能状态
Result result[] = {
   '1', '2', '3', '4', '5', '6'};   // 所有的可能符号
double init[] = {
   0.9, 0.1};    // 初始状态的概率向量
double emission[][6] = {
      // 发射矩阵:行对应着状态,列对应着符号
  1.0/6, 1.0/6, 1.0/6, 1.0/6, 1.0/6, 1.0/6,
  0.1, 0.1, 0.1, 0.1, 0.1, 0.5
};
double trans[][2] = {
      // 转移矩阵:行和列都是状态
  0.95, 0.05,
  0.1, 0.9
};
const int nstate = 2;
const int nresult = 6;

State* rst;   // 一串随机状态序列
Result* rres;  // 一串随机符号序列
State* vst;   // viterbi算法猜出来的状态序列

struct Unit {
   
  double v;
  int *p;
  int size;
};
typedef struct Unit* pUnit;

int random(double* prob, const int n);
void randSeq(State* st, Result* res, const int n);
void printState(State* st, const int n);
void printResult(Result* res, const int n);
int getResultIndex(Result r);
void traceback(pUnit** a, const int l, const int i, State* st, const int m, int n);
void viterbi(Result* res, State* gst, const int n);

int main(void) {
   
  int i;
  int n = 300;
  if ((rst = (State*) malloc(sizeof(State) * n)) == NULL || \
      (rres = (Result*) malloc(sizeof(Result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值