www.pudn.com > hmmPlatform.rar > HMM.cpp
#include "StdAfx.h" #include ".\hmm.h" #include////////////////////////////////////////////////////////////////////////// // 构造函数 // // 创建人: 陈文凯 // 创建日期: 2005-06-05 // 修改人: // 修改日期: CHMM::CHMM() { // 设定数据 this->m_pPi = NULL; this->m_pA = NULL; this->m_pB = NULL; this->m_pCodeBook = NULL; } ////////////////////////////////////////////////////////////////////////// // 析构函数 // // 创建人: 陈文凯 // 创建日期: 2005-06-05 // 修改人: // 修改日期: CHMM::~CHMM(void) { this->Dispose(); } ////////////////////////////////////////////////////////////////////////// // 释放模型占用资源 // // 创建人: 陈文凯 // 创建日期: 2005-06-09 // 修改人: // 修改日期: void CHMM::Dispose() { if (this->m_pA != NULL) { delete[] this->m_pA; this->m_pA = NULL; } if (this->m_pB != NULL) { delete[] this->m_pB; this->m_pB = NULL; } if (this->m_pPi != NULL) { delete[] this->m_pPi; this->m_pPi = NULL; } if (this->m_pCodeBook != NULL) { delete[] this->m_pCodeBook; this->m_pCodeBook = NULL; } } ////////////////////////////////////////////////////////////////////////// // 导出HMM模型信息 // // 创建人: 陈文凯 // 创建日期: 2005-06-09 // 修改人: // 修改日期: BOOL CHMM::SaveModel(CString strFileName) { CFile outFile(strFileName, CFile::modeCreate | CFile::modeWrite); // 操作是否成功 BOOL bRet = FALSE; if (outFile.m_hFile != CFile::hFileNull) { // 写入数字 outFile.Write((LPCTSTR)this->m_strWord, CHMM_WORD_LEN); // 写入HMM状态数 outFile.Write(&this->m_nStatusNums, sizeof(unsigned int)); // 写入短时点数 outFile.Write(&this->m_nFrameSize, sizeof(unsigned int)); // 写入训练码本长度 outFile.Write(&this->m_nCodeNums, sizeof(unsigned int)); // 写入Pi序列 outFile.Write(this->m_pPi, sizeof(double) * this->m_nStatusNums); // 写入A序列 outFile.Write(this->m_pA, sizeof(double) * this->m_nStatusNums * this->m_nStatusNums); // 写入B序列 outFile.Write(this->m_pB, sizeof(double) * this->m_nStatusNums * this->m_nCodeNums); // 写入码本 outFile.Write(this->m_pCodeBook, sizeof(double) * this->m_nCodeNums); // 关闭导出文件 outFile.Close(); bRet = TRUE; } return bRet; } ////////////////////////////////////////////////////////////////////////// // 导入HMM模型信息 // // 创建人: 陈文凯 // 创建日期: 2005-06-09 // 修改人: // 修改日期: BOOL CHMM::LoadModel(CString strFileName) { CFile inFile(strFileName, CFile::modeRead | CFile::shareDenyNone); // 操作是否成功 BOOL bRet = FALSE; // 保存读入数字 char strWord[CHMM_WORD_LEN]; if (inFile.m_hFile != CFile::hFileNull) { // 释放模型占用资源 this->Dispose(); // 读入数字 inFile.Read(&strWord, CHMM_WORD_LEN); this->m_strWord.Empty(); this->m_strWord.Append(strWord, CHMM_WORD_LEN); // 读入HMM状态数 inFile.Read(&this->m_nStatusNums, sizeof(unsigned int)); // 读入短时点数 inFile.Read(&this->m_nFrameSize, sizeof(unsigned int)); // 读入训练码本长度 inFile.Read(&this->m_nCodeNums, sizeof(unsigned int)); // 分配参数空间 this->m_pPi = new double[this->m_nStatusNums]; this->m_pA = new double[this->m_nStatusNums * this->m_nStatusNums]; this->m_pB = new double[this->m_nStatusNums * this->m_nCodeNums]; this->m_pCodeBook = new double[this->m_nCodeNums]; // 读入Pi序列 inFile.Read(this->m_pPi, sizeof(double) * this->m_nStatusNums); // 读入A序列 inFile.Read(this->m_pA, sizeof(double) * this->m_nStatusNums * this->m_nStatusNums); // 读入B序列 inFile.Read(this->m_pB, sizeof(double) * this->m_nStatusNums * this->m_nCodeNums); // 读入码本 inFile.Read(this->m_pCodeBook, sizeof(double) * this->m_nCodeNums); // 关闭导入文件 inFile.Close(); bRet = TRUE; } return bRet; } ////////////////////////////////////////////////////////////////////////// // 实现对HMM模型的迭代训练 // // 创建人: 陈文凯 // 创建日期: 2005-06-09 // 修改人: // 修改日期: void CHMM::Train( const double* pDataIn, // 输入采样序列 unsigned int nInLen // 输入采样序列长度 ) { // 前后向算法返回参数 double fRate = 0; double* pa = NULL; double* pb = NULL; // 对输入采样序列进行矢量量化形成的码本 double* pCodeBook = NULL; // 输入码本对应的HMM模型下标 unsigned int* pCodeIndex = NULL; // 判断输入数据是否合法 if (pDataIn != NULL && this->m_pA != NULL && this->m_pB != NULL && this->m_pPi != NULL) { pa = new double[this->m_nCodeNums * this->m_nStatusNums]; pb = new double[this->m_nCodeNums * this->m_nStatusNums]; // 对输入采样序列进行矢量量化 pCodeBook = new double[this->m_nCodeNums]; CVQ::KMeansCluster( pDataIn, nInLen, pCodeBook, this->m_nCodeNums); // 获得输入码本对应的HMM模型码本下标 pCodeIndex = new unsigned int[this->m_nCodeNums]; CVQ::Classify( pCodeBook, this->m_nCodeNums, this->m_pCodeBook, this->m_nCodeNums, pCodeIndex); // 调用前后向算法, 计算概率, pa, pb fRate = this->ForwardBackward( pCodeIndex, this->m_nCodeNums, this->m_pPi, this->m_pA, this->m_pB, this->m_nStatusNums, pa, pb); // 调用BW算法进行参数优化 this->BaumWelch( pCodeIndex, this->m_nCodeNums, this->m_pPi, this->m_pA, this->m_pB, this->m_nStatusNums, pa, pb, fRate); // 更新码本 for (unsigned int i = 0; i < this->m_nCodeNums; i++) { // this->m_pCodeBook[i] = (this->m_pCodeBook[i] + pCodeBook[i]) / 2; this->m_pCodeBook[i] = this->m_pCodeBook[i] * 0.7 + pCodeBook[i] * 0.3; } delete[] pa; delete[] pb; delete[] pCodeBook; delete[] pCodeIndex; } } ////////////////////////////////////////////////////////////////////////// // 实现对HMM模型得初始化训练 // // 创建人: 陈文凯 // 创建日期: 2005-06-09 // 修改人: // 修改日期: void CHMM::PrepareTrain( CString strWord, const double* pDataIn, // 第一个输入采样序列 unsigned int nInLen, // 输入采样序列长度 unsigned int nFrameSize,// 分帧宽度 unsigned int nStatusNums /* = 6 */, unsigned int nCodeNums /* = 4 */ ) { // 计算概率 double fRate = 0; // 循环变量 unsigned int i = 0; unsigned int j = 0; // 设置模型信息 this->m_strWord = strWord; this->m_nStatusNums = nStatusNums; this->m_nCodeNums = nCodeNums; this->m_nFrameSize = nFrameSize; // 初始化pI/pA/pB/pCodeBook this->m_pPi = new double[this->m_nStatusNums]; this->m_pA = new double[this->m_nStatusNums * this->m_nStatusNums]; this->m_pB = new double[this->m_nStatusNums * this->m_nCodeNums]; this->m_pCodeBook = new double[this->m_nCodeNums]; memset(this->m_pPi, 0, sizeof(double) * this->m_nStatusNums); memset(this->m_pA, 0, sizeof(double) * this->m_nStatusNums * this->m_nStatusNums); memset(this->m_pB, 0, sizeof(double) * this->m_nStatusNums * this->m_nCodeNums); memset(this->m_pCodeBook, 0, sizeof(double) * this->m_nCodeNums); // 对pI进行均分 fRate = (double) 1 / nStatusNums; for (i = 0; i < nStatusNums; i++) { this->m_pPi[i] = fRate; } // 对PA[i][j]进行均分 for (i = 0; i < nStatusNums; i++) { for (j = 0; j < nStatusNums; j++) { this->m_pA[i * nStatusNums + j] = fRate; } } // 对pB[i][j]进行均分 fRate = (double) 1 / nCodeNums; for (i = 0; i < nStatusNums; i++) { for (j = 0; j < nCodeNums; j++) { this->m_pB[i * nCodeNums + j] = fRate; } } // 形成初始码本 CVQ::KMeansCluster(pDataIn, nInLen, this->m_pCodeBook, this->m_nCodeNums); // 进行参数优化 this->Train(pDataIn, nInLen); } ////////////////////////////////////////////////////////////////////////// // 实现前后向算法,对于给定的HMM参数和观察序列O, // 求能够产生观察序列O的概率 // // 创建人: 陈文凯 // 创建日期: 2005-06-08 // 修改人: // 修改日期: double CHMM::ForwardBackward( const unsigned int* pCodeBook, // 输入观察码本序列对应的HMM码本的下标 unsigned int nCodeNums, // 输入观察序列长度 const double* pPi, // HMM的pi矢量 const double* pA, // HMM的A矩阵 const double* pB, // HMM的B矩阵 unsigned int nStatusNums, // HMM状态数 double* pa, // 前后向算法中的a double* pb // 前后向算法中的b ) { // 能够产生观察序列O的概率 double fRate = 0; // 递推时做累积 double fSum = 0; // 循环变量,damn vc // 输入序列循环 int t = 0; // 状态循环 int i = 0; int j = 0; // 初始化pa, pb memset(pa, 0, sizeof(double) * (nCodeNums * nStatusNums)); memset(pb, 0, sizeof(double) * (nCodeNums * nStatusNums)); // 实现前后向算法 // 初始化 for (i = 0; i < nStatusNums; i++) { // 计算公式:pa[1][i] = pI[i] * pB[i][o1] 1 <= i <= N pa[0 * nStatusNums + i] = pPi[i] * pB[i * nCodeNums + pCodeBook[0]]; // 计算公式:pb[nCodeNums - 1][i] = 1 pb[(nCodeNums - 1) * nStatusNums + i] = 1; } // 前向递推 for (t = 1; t < nCodeNums; t++) { for (j = 0; j < nStatusNums; j++) { fSum = 0; for (i = 0; i < nStatusNums; i++) { // 累积 fSum += pa[t-1][i] * pA[i][j] fSum += pa[(t - 1) * nStatusNums + i] * pA[i * nStatusNums + j]; } // 计算公式:pa[t][j] = fSum * pB[j][ot] pa[t * nStatusNums + j] = fSum * pB[j * nCodeNums + pCodeBook[t]]; TRACE("pa(%d, %d) : %f\n", t, j, pa[t * nStatusNums + j]); } } // 后向递推 for (t = nCodeNums - 2; t >= 0; t--) { for (i = 0; i < nStatusNums; i++) { fSum = 0; for (j = 0; j < nStatusNums; j++) { // 累积 fSum += pA[i][j] * pB[j][t + 1] * pb[t + 1][j] fSum += pA[i * nStatusNums + j] * pB[j * nCodeNums + pCodeBook[t + 1]] * pb[(t + 1) * nStatusNums + j]; } pb[t * nStatusNums + i] = fSum; TRACE("pb(%d, %d): %lf\n", t, i, pb[t * nStatusNums + i]); } } // 结束 // 计算概率,令t=nCodeNums-1 t = nCodeNums - 1; for (i = 0; i < nStatusNums; i++) { // 计算公式:fRate += pa[t][i] * pb[t][i]; fRate += pa[t * nStatusNums + i] * pb[t * nStatusNums + i]; } return fRate; } ////////////////////////////////////////////////////////////////////////// // 实现Viterbi算法,对于给定的HMM参数和观察序列O, // 返回最大概率 // // 创建人: 陈文凯 // 创建日期: 2005-06-08 // 修改人: // 修改日期: double CHMM::Viterbi( const unsigned int* pCodeBook, // 输入观察码本序列对应的HMM码本的下标 unsigned int nCodeNums, // 输入观察序列长度 const double* pPi, // HMM的pi矢量 const double* pA, // HMM的A矩阵 const double* pB, // HMM的B矩阵 unsigned int nStatusNums // HMM状态数 ) { // pa[t][i]为t时刻沿着一条路径q1, q2, ..., qt,且qt = st // 产生出输入观察序列o1, o2, o3, ..., ot的最大概率 // 1 <= t <= nInLen, 1 <= i <= nStatusNums double* pa = NULL; // 临时计算 double fTemp = 0; double fMax = 0; // 最大概率 double fMaxRate = 0; // 循环变量 // 采样序列循环 unsigned int t = 0; // 状态循环 unsigned int j = 0; unsigned int i = 0; // 分配和初始化pa pa = new double[nCodeNums * nStatusNums]; memset(pa, 0, sizeof(double) * (nCodeNums * nStatusNums)); // 初始化 for (i = 0; i < nStatusNums; i++) { // 计算公式:pa[1][i] = pi[i] * pB[i][1] pa[0 * nStatusNums + i] = pPi[i] * pB[i * nCodeNums + pCodeBook[0]]; } // 递推 for (t = 1; t < nCodeNums; t++) { for (j = 0; j < nStatusNums; j++) { // 求pa[t - 1][i] * pA[i][j]的最大值, 1 <= i <= N fMax = pa[(t - 1) * nStatusNums + 0] * pA[0 * nStatusNums + j]; for (i = 0; i < nStatusNums; i++) { fTemp = pa[(t - 1) * nStatusNums + i] * pA[i * nStatusNums + j]; fMax = (fMax < fTemp ? fTemp : fMax); } // 计算pa[t][j]和pb[t][j] // 计算公式:pa[t][j] = fMax * pB[j][t]; pa[t * nStatusNums + j] = fMax * pB[j * nCodeNums + pCodeBook[t]]; } } // 终结 // 求最大概率, fMaxRate = max(pa[nCodeNums - 1][i]) 1 <= i <= nStatusNums fMaxRate = pa[(nCodeNums - 1) * nStatusNums + 0]; for (i = 0; i < nStatusNums; i++) { fTemp = pa[(nCodeNums - 1) * nStatusNums + i]; fMaxRate = (fMaxRate < fTemp ? fTemp : fMaxRate); } delete[] pa; return fMaxRate; } ////////////////////////////////////////////////////////////////////////// // 实现Baum Welch算法,对于给定的观察序列O, // 求使P(O | HMM参数)最大的HMM参数pi, A, B // // 创建人: 陈文凯 // 创建日期: 2005-06-08 // 修改人: // 修改日期: void CHMM::BaumWelch( const unsigned int* pCodeBook, // 输入观察码本序列对应的HMM码本的下标 unsigned int nCodeNums, // 输入观察序列长度 double* pPi, // HMM的pi矢量 double* pA, // HMM的A矩阵 double* pB, // HMM的B矩阵 unsigned int nStatusNums, // HMM状态数 double* pa, // 前后向算法计算所得的pa double* pb, // 前后向算法计算所得pb double fRate // 前后向算法计算所得最大概率 ) { // bw算法中概率矩阵 // pRate[nCodeNums][nStatusNums][nStatusNums] double* pRate = NULL; // 最小概率 double fMinRate = 0.0001; unsigned int nOffSet = nStatusNums * nStatusNums; // 循环变量 unsigned int t = 0; unsigned int i = 0; unsigned int j = 0; unsigned int k = 0; // 求概率时的分子 double fAvgUp = 0; // 求概率时的分母 double fAvgDown = 0; // 数组元素下标 unsigned int nIndex = 0; // 初始化概率矩阵 pRate = new double[nCodeNums * nOffSet]; memset(pRate, 0, sizeof(double) * nCodeNums * nOffSet); // 计算pRate[t][i][j],即t时刻Markov链处于i状态, t+1时刻处于j状态的概率 for (t = 0; t < (nCodeNums - 1); t++) { for (i = 0; i < nStatusNums; i++) { for (j = 0; j < nStatusNums; j++) { pRate[t * nOffSet + i * nStatusNums + j] = (pa[t * nStatusNums + i] * pA[i * nStatusNums + j] * pB[j* nCodeNums + pCodeBook[t + 1]] * pb[(t + 1) * nStatusNums + j]) / fRate; TRACE("pRate[%d, %d, %d]: %f\n", t, i, j, pRate[t * nOffSet + i * nStatusNums + j]); TRACE("pa: %f, pb: %f, pA: %f, pB: %f\n", pa[t * nStatusNums + i], pA[i * nStatusNums + j], pB[j* nCodeNums + pCodeBook[t + 1]], pb[(t + 1) * nStatusNums + j] ); } } } // 使用重估公式计算HMM参数 // 计算Pi矢量, 即0时刻时Markov链处于i状态, t+1时刻处于其他任何状态的概率 for (i = 0; i < nStatusNums; i++) { pPi[i] = 0; for (j = 0; j < nStatusNums; j++) { pPi[i] += pRate[0 * nOffSet + i * nStatusNums + j]; } pPi[i] = (pPi[i] > 0.0 ? pPi[i] : fMinRate); TRACE("pi[%d]: %f\n", i, pPi[i]); } // 计算A矩阵 // pA[i][j] = (从状态Si过渡到Sj的平均次数) / (从状态Si向其他状态转移的平均次数) for (i = 0; i < nStatusNums; i++) { for (j = 0; j < nStatusNums; j++) { // 计算状态Si过渡到Sj的平均次数 fAvgUp = 0; // 从状态Si向其他状态转移的平均次数 fAvgDown = 0; for (t = 0; t < nCodeNums; t++) { fAvgUp += pRate[t * nOffSet + i * nStatusNums + j]; for (k = 0; k < nStatusNums; k++) { fAvgDown += pRate[t * nOffSet + i * nStatusNums + k]; } } nIndex = i * nStatusNums + j; pA[nIndex] = fAvgUp / fAvgDown; pA[nIndex] = (pA[nIndex] > 0.0 ? pA[nIndex] : fMinRate); TRACE("pA[%d,%d]: %f\n", i, j, pA[nIndex]); } } // 计算B矩阵 // pB[i][k] = (出现状态i和观察值Ot=Vk的平均次数) / (处于状态i的次数) for (i = 0; i < nStatusNums; i++) { for (k = 0; k < nCodeNums; k++) { // 计算出现状态i和观察值Ot=Vk的平均次数 fAvgUp = 0; for (t = 0; t < nCodeNums; t++) { if (pCodeBook[t] == pCodeBook[k]) { for (j = 0; j < nStatusNums;j ++) { fAvgUp += pRate[t * nOffSet + i * nStatusNums + j]; } } } // 处于状态i的次数 fAvgDown = 0; for (t = 0; t < nCodeNums; t++) { for (j = 0; j < nStatusNums; j++) { fAvgDown += pRate[t * nOffSet + i * nStatusNums + j]; } } // 计算pB[i][k] nIndex = i * nCodeNums + pCodeBook[k]; pB[nIndex] = fAvgUp / fAvgDown; pB[nIndex] = (pB[nIndex] > 0.0 ? pB[nIndex] : fMinRate); TRACE("pB[%d, %d]: %f\n", i, j, pB[nIndex]); } } // 释放资源 if (pRate != NULL) { delete[] pRate; } } ////////////////////////////////////////////////////////////////////////// // 基于Vertibi算法实现对HMM模型的识别 CString CHMM::RecogonizeByViterbi( const double* pCodeBook, // 输入码本 unsigned int nCodeNums, // 输入码本长度 CString* strModelList, // 模型文件路径 unsigned int nModelCount // 模型数量 ) { CString strWord; // 最大概率 double fMaxRate = 0; // 识别概率 double fRate = 0; // 模型 CHMM hmmModel; // 输入码本对应的HMM码本下标 unsigned int* pCodeIndex = NULL; if (pCodeBook != NULL && strModelList != NULL) { // 初始化输入码本对应的HMM码本下标 pCodeIndex = new unsigned int[nCodeNums]; for (unsigned int i = 0; i < nModelCount; i++) { if (hmmModel.LoadModel(strModelList[i])) { // 获取输入码本对应的HMM码本下标 CVQ::Classify( pCodeBook, nCodeNums, hmmModel.m_pCodeBook, hmmModel.m_nCodeNums, pCodeIndex); fRate = hmmModel.Viterbi( pCodeIndex, nCodeNums, hmmModel.m_pPi, hmmModel.m_pA, hmmModel.m_pB, hmmModel.m_nStatusNums); // 保存最大概率 if (fMaxRate < fRate) { fMaxRate = fRate; // 保存最大概率模型对应单词 strWord = hmmModel.GetWord(); } } } delete[] pCodeIndex; } return strWord; } ////////////////////////////////////////////////////////////////////////// // 基于DTW的Vertibi实现对HMM模型的识别 CString CHMM::RecogonizeByDTW( const double* pCodeBook, // 输入码本 unsigned int nCodeNums, // 输入码本长度 CString* strModelList, // 模型文件路径 unsigned int nModelCount // 模型数量 ) { CString strWord; // 最大概率 double fMaxRate = 0; // 识别概率 double fRate = 0; // 模型 CHMM hmmModel; // 输入码本对应的HMM码本下标 unsigned int* pCodeIndex = NULL; if (pCodeBook != NULL && strModelList != NULL) { // 初始化输入码本对应的HMM码本下标 pCodeIndex = new unsigned int[nCodeNums]; for (unsigned int i = 0; i < nModelCount; i++) { if (hmmModel.LoadModel(strModelList[i])) { // 获取输入码本对应的HMM码本下标 CVQ::Classify( pCodeBook, nCodeNums, hmmModel.m_pCodeBook, hmmModel.m_nCodeNums, pCodeIndex); fRate = hmmModel.DTW( pCodeIndex, nCodeNums, hmmModel.m_pA, hmmModel.m_pB, hmmModel.m_nStatusNums); // 保存最大概率 if (fMaxRate < fRate) { fMaxRate = fRate; // 保存最大概率模型对应单词 strWord = hmmModel.GetWord(); } } } delete[] pCodeIndex; } return strWord; } ////////////////////////////////////////////////////////////////////////// // 实现基于VQ的HMM模型识别 CString CHMM::RecogonizeByVQ( const double* pCodeBook, // 输入码本 unsigned int nCodeNums, // 输入码本长度 CString* strModelList, // 模型文件路径 unsigned int nModelCount // 模型数量 ) { CString strWord; // 最小距离 double fMinDis = 30000; // 码本距离 double fDis = 0; // 最小距离模型下标 unsigned int nMinModel = 0; // 模型 CHMM hmmModel; if (pCodeBook != NULL && strModelList != NULL) { for (unsigned int i = 0; i < nModelCount; i++) { if (hmmModel.LoadModel(strModelList[i])) { fDis = CVQ::GetDistance(pCodeBook, hmmModel.m_pCodeBook, nCodeNums); // 保存最小距离 if (fDis < fMinDis) { fMinDis = fDis; // 保存最小距离模型对应单词 strWord = hmmModel.GetWord(); TRACE("%d, %f\n", i, fMinDis); } } } } return strWord; } ////////////////////////////////////////////////////////////////////////// // 实现基于DTW的Viterbi算法 double CHMM::DTW( const unsigned int* pCodeBook, // 输入观察码本序列对应的HMM码本的下标 unsigned int nCodeNums, // 输入观察序列长度 double* pA, // HMM的A矩阵 double* pB, // HMM的B矩阵 unsigned int nStatusNums // HMM状态数 ) { double* pRate = NULL; // 返回的D(nStatusNums, nInLen) double fRetRate = 0; // 计算最大D + A double fMaxD = 0; double fTemp1 = 0; double fTemp2 = 0; if (pCodeBook != NULL) { // 初始话概率矩阵 pRate = new double[nCodeNums * nStatusNums]; memset(pRate, 0, sizeof(double) * nCodeNums * nStatusNums); for (unsigned int i = 0; i < nStatusNums; i++) { for (unsigned int j = 1; j < nCodeNums; j++) { // 计算D(i-1, j) + A(j, j) fTemp1 = ((i - 1) >= 0 ? pRate[(i - 1) * nStatusNums + j] : 0); fTemp1 += pA[j * nStatusNums + j]; // 计算D(i-1, j-1) + A(j-1, j); fTemp2 = ((i - 1) >= 0 && (j - 1) >= 0 ? pRate[(i - 1) * nStatusNums + (j - 1)] : 0); fTemp2 += ((j - 1) >= 0 ? pA[(j - 1) * nStatusNums + j] : 0); // 取D(i, j) = B(i, j) + max(fTemp1, fTemp2); fMaxD = (fTemp1 > fTemp2 ? fTemp1 : fTemp2); pRate[i * nCodeNums + j] = pB[i * nCodeNums + pCodeBook[j]] + fMaxD; } } fRetRate = pRate[(nCodeNums - 1) * nStatusNums + (nStatusNums - 1)]; // 释放所占用资源 delete[] pRate; } return fRetRate; }