和分治法一样,动态规划是通过组合子问题的解而解决整个问题的。分治法是指将问题划分成一些独立的子问题,递归地求解各个子问题,然后合并子问题的解而得到原问题的解。与此不同的是,动态规划适用于子问题不是独立的情况,也就是各个子问题包含公共的子子问题。动态规划算法对每个子子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。
动态规划求解问题一般有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
这篇写得很不错,至今是我看过最通俗易懂的,大家可以看看这篇。