动态规划随笔

和分治法一样,动态规划是通过组合子问题的解而解决整个问题的。分治法是指将问题划分成一些独立的子问题,递归地求解各个子问题,然后合并子问题的解而得到原问题的解。与此不同的是,动态规划适用于子问题不是独立的情况,也就是各个子问题包含公共的子子问题。动态规划算法对每个子子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。

动态规划求解问题一般有2个性质:

(1)最优子结构:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

(2)重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。

一、最长公共子序列(LCS)

定义c[i,j]为序列Xi和Yj的一个LCS的长度。由LCS的最优子结构可得递归式:


import java.util.LinkedList;
import java.util.Scanner;

/**
 * 最长公共子序列
 * 
 * @version 1.60 2014-6-12
 */
public class LCS {
	private int[][] c;// 记录LCS的长度
	private Location[][] path;// 记录LCS的路径

	/**
	 * 计算LCS
	 * 
	 * @param X
	 *            X字符串
	 * @param Y
	 *            Y字符串
	 */
	public void getLCS(char[] X, char[] Y) {
		if (X == null || X.length == 0 || Y == null || Y.length == 0)
			return;

		int lengthX = X.length;
		int lengthY = Y.length;
		c = new int[lengthX + 1][lengthY + 1];
		path = new Location[lengthX + 1][lengthY + 1];

		// 给Xi=0或Yj=0的位置赋初值0
		for (int i = 0; i <= lengthX; i++)
			c[i][0] = 0;
		for (int j = 0; j <= lengthY; j++)
			c[0][j] = 0;

		// 计算LCS
		for (int i = 1; i <= lengthX; i++) {
			for (int j = 1; j <= lengthY; j++) {
				if (X[i - 1] == Y[j - 1]) {
					c[i][j] = c[i - 1][j - 1] + 1;
					path[i][j] = new Location(i - 1, j - 1);
				} else {
					if (c[i][j - 1] > c[i - 1][j]) {
						c[i][j] = c[i][j - 1];
						path[i][j] = new Location(i, j - 1);
					} else {
						c[i][j] = c[i - 1][j];
						path[i][j] = new Location(i - 1, j);
					}
				}
			}
		}

		printPath(X, lengthX, lengthY);
	}

	/**
	 * 输出LCS
	 * 
	 * @param lengthX
	 *            X字符数组长度
	 * @param lengthY
	 *            Y字符数组长度
	 */
	private void printPath(char[] X, int lengthX, int lengthY) {
		// TODO Auto-generated method stub
		Location l = path[lengthX][lengthY];
		LinkedList<Character> stack = new LinkedList<Character>();
		int currentX = lengthX;
		int currentY = lengthY;
		while (currentX != 0 && currentY != 0) {
			if (currentX == l.x + 1 && currentY == l.y + 1)
				stack.push(X[currentX - 1]);

			currentX = l.x;
			currentY = l.y;
			l = path[currentX][currentY];
		}

		while (!stack.isEmpty()) {
			System.out.print(stack.pop() + " ");
		}
		System.out.println();
	}

	/**
	 * 记录数组位置
	 * 
	 * @version 1.60 2014-6-12
	 * @author 王曙光
	 */
	private static class Location {
		int x;
		int y;

		Location(int x, int y) {
			this.x = x;
			this.y = y;
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner cin = new Scanner(System.in);
		LCS lcs = new LCS();

		while (cin.hasNext()) {
			String sX = cin.next();
			String sY = cin.next();
			char[] X = sX.toCharArray();
			char[] Y = sY.toCharArray();
			lcs.getLCS(X, Y);
		}
	}

}

二、HMM(隐马尔科夫模型)的维特比算法

隐马尔科夫模型可参考博客:http://blog.csdn.net/likelet/article/details/7056068

维特比算法可参考:http://www.52nlp.cn/hmm-learn-best-practices-six-viterbi-algorithm-1

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.LinkedList;
import java.util.Scanner;

import com.jingguangzhilu.HMM.HMMStruct;
import com.jingguangzhilu.HMM.ViterbiAlgorith;

/**
 * 维特比算法
 * 
 * @version 1.60 2014-6-13
 */
public class Viterbi {
	private int N;// 隐藏状态数目;Q={1,2,...,N}
	private int M;// 观察符号数目;V={1,2,...,M}
	private double[][] A;// 状态转移矩阵A[1...N][1...N].a[i][j]是从t时刻状态i到t+1时刻状态j的转移概率
	private double[][] B;// 混淆矩阵B[1..N][1..M]. b[j][k]在状态j时观察到符合k的概率。
	private double[] pi;// 初始向量pi[1..N],pi[i] 是初始状态概率分布

	private double[][] delta;// 局部最优概率
	private int[][] path;// 记录所有回溯路径
	private int[] resultPath;// 记录最后得出的最有可能路径

	public Viterbi(int N, int M, double[][] A, double[][] B, double[] pi) {
		this.N = N;
		this.M = M;
		this.A = A;
		this.B = B;
		this.pi = pi;
	}

	/**
	 * 维特比算法
	 * 
	 * @param sequence
	 *            观测序列
	 */
	public void viterbi(int[] sequence) {
		if (sequence == null || sequence.length == 0)
			return;

		int seqLen = sequence.length;
		delta = new double[seqLen][N];
		path = new int[seqLen][N];

		// 初始化:计算t=0时的所有状态的局部局部概率
		for (int i = 0; i < N; i++) {
			delta[0][i] = pi[i] * B[i][sequence[0]];
			path[0][i] = -1;// 初始位置的前一个位置不存在,则赋值-1
		}
		// 归纳:递归计算每个时间点,t=2,3,...,T时的最优局部概率
		for (int t = 1; t < seqLen; t++) {
			for (int i = 0; i < N; i++) {
				double maxProbability = -1;
				int maxPosition = -1;
				for (int j = 0; j < N; j++) {
					double tempProbability = delta[t - 1][j] * A[j][i];
					if (Double.compare(tempProbability, maxProbability) > 0) {
						maxProbability = tempProbability;
						maxPosition = j;
					}

				}

				delta[t][i] = maxProbability * B[i][sequence[t]];
				path[t][i] = maxPosition;
			}
		}

		// 终止:最可能状态序列
		resultPath = new int[seqLen];
		double lastMaxProb = -1;
		for (int i = 0; i < N; i++) {
			if (Double.compare(delta[seqLen - 1][i],  lastMaxProb)>0) {
				lastMaxProb = delta[seqLen - 1][i];
				resultPath[seqLen - 1] = i;
			}
		}

		for (int t = seqLen - 2; t >= 0; t--) {
			resultPath[t] = path[t + 1][resultPath[t + 1]];
		}
		
		//输出
		for (int i = 0; i <seqLen ; i++)
			System.out.print(resultPath[i]+" ");
		System.out.println();

	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner cin = null;
		try {
			cin = new Scanner(new FileReader("hmm_viterbi2.txt"));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		int M = 0, N = 0;
		double[][] A = null, B = null;
		double[] pi = null;
		int[] O = null;
		int T = 0;

		while (cin.hasNext()) {
			M = cin.nextInt();
			N = cin.nextInt();

			A = new double[N][N];
			B = new double[N][M];
			pi = new double[N];

			for (int i = 0; i < N; i++)
				for (int j = 0; j < N; j++)
					A[i][j] = cin.nextDouble();

			for (int i = 0; i < N; i++)
				for (int j = 0; j < M; j++)
					B[i][j] = cin.nextDouble();

			for (int i = 0; i < N; i++)
				pi[i] = cin.nextDouble();

			T = cin.nextInt();
			O = new int[T];
			for (int i = 0; i < T; i++)
				O[i] = cin.nextInt();

		}

		Viterbi viterbi = new Viterbi(N, M, A, B, pi);
		viterbi.viterbi(O);

		
	}

}

文件hmm_viterbi2.txt内容:

2


3


0.333 0.333 0.333
0.333 0.333 0.333
0.333 0.333 0.333


0.5 0.5
0.75 0.25
0.25 0.75


0.333 0.333 0.333


10
0 0 0 0 1 0 1 1 1 1


三、01背包

转自:http://blog.csdn.net/insistgogo/article/details/8579597

这篇写得很不错,至今是我看过最通俗易懂的,大家可以看看这篇。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值