当前位置:文档之家› 动态规划:卷积码的Viterbi译码算法

动态规划:卷积码的Viterbi译码算法

动态规划:卷积码的Viterbi译码算法学院:网研院ﻩ姓名:xxx 学号:xxx一、动态规划原理动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。

动态规划算法通常用于求解具有某种最优性质的问题。

在这类问题中,可能会有许多可行解,每一个解都对应于一个值,我们希望找到具有最优值的解。

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。

若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。

如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。

动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。

不象搜索或数值计算那样,具有一个标准的数学表达式和明确清晰的解题方法。

动态规划程序设计往往是针对一种最优化问题,由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。

二、卷积码的Viterbi译码算法简介在介绍维特比译码算法之前,首先了解一下卷积码编码,它常常与维特比译码结合使用。

(2,1,3)卷积码编码器是最常见的卷积码编码器,在本次实验中也使用了(2,1,3)卷积码编码器,下面介绍它的原理。

(2,1,3)卷积码是把信源输出的信息序列,以1个码元为一段,通过编码器输出长为2的一段码段。

该码段的值不仅与当前输入码元有关,而且也与其之前的2个输入码元有关。

如下图所示,输出out1是输入、第一个编码器存储的值和第二个编码器存储的值逻辑加操作的结果,输出out2是输入和第二个编码器存储的值逻辑加操作的结果。

(2,1,3)卷积码编码器(2,1,3)卷积码编码器的状态转移图如下所示,圆圈中的数字表示当前编码器中的状态,箭头指向下一个可能的状态,箭头边上的数字是状态转移对应的输出out1、out2。

(2,1,3)卷积码编码器状态转移图维特比译码中使用了汉明距离的概念,下面了解一下汉明距离的原理。

汉明距离是两个等长字符串对应位置的字符不同的个数,如0 1 1 0 0 1 0 0和0 0 1 1 1 0 0 0的汉明距离是4。

维比特译码的基本思想是把接收到的矢量,和网格图上诸种可能的路径比较,删去距离大的路径,保留距离小的路径,以距离最小路径作为发码的估值。

如输入信息比特1 0 0 1,则(2,1,3)卷积码编码器输出的信息比特是1 1 1 0 1 1 1 1,假设经过信道干扰后接收到的信息比特是1 1 0 0 1 1 1 1,则维特比译码过程是:1.从T0时刻的全零状态开始,零状态初始度量(汉明距离)为0,其它状态初始度量为正无穷;2.在任一时刻t向t+1时刻前进,对每一个状态只记录到达路径中度量最小的一个(残留路径)及其度量;3.前进过程中,对t时刻的每个状态作延伸,即在状态度量基础上加上分支度量,得到8条路径;4.在t+1时刻,对到达每一个状态的2条路径进行比较,找到一个度量最小的作为残留路径;5.直到码的终点,如果终点是一个确定状态,则最终保留的路径就是译码结果。

维特比译码算法执行过程三、卷积码的Viterbi译码算法1.算法●输入比特流个数、比特流和有噪信道的误码率(0~1);●对比特流数据进行补0操作(在比特流的最后编码器中仍保存着2个之前输入比特的状态,因此需要进行补0操作,即给输入比特流加上2个0比特);●对补0后的比特流进行(2,1,3)卷积码编码操作,编码输出的第一个结果是输入、第一个编码器存储的值和第二个编码器存储的值逻辑加操作的结果,第二个结果是输入和第二个编码器存储的值逻辑加操作的结果;●对(2,1,3)卷积码编码输出的数据进行传输(加上误码);●对从信道得到的有误码的比特流进行维特比译码:•对比特流进行分组,2个一组循环;•根据这2个比特对当前的4个状态(StateNode)计算从它出发到它可能到达的2个状态对应路径的汉明距离,并保存对应的译码序列和汉明距离;•根据上一步的结果,取汉明距离小的更新这4个状态;•最后,第1个状态(0状态)对应的译码序列就是维特比译码的结果(因为补零操作保证了最后肯定会回到0状态)。

2.算法复杂度假设输入比特流序列的长度为L。

由于(2,1,3)卷积码的状态数是4,对每个时刻要做4次“加-比-选”操作得到4个状态的残留路径,每次“加-比-选”操作包括2次加法和1次比较,因此总运算量约为4L次“加-比-选”操作。

同时要能保存4条残留路径,因此需要4L个存贮单元。

由此可见,(2,1,3)卷积码的维特比译码算法的时间和空间复杂度均与比特流序列长度呈线性关系,但维特比译码算法的时间空间复杂度与卷积码的约束长度呈指数关系。

3.可能的改进由于维特比译码算法的时间空间复杂度与卷积码的约束长度呈指数关系,因此对状态数很大的卷积码编码,维特比算法要经一定的修正后才可能实用,常用的算法是缩减状态的维特比译码,即在每一时刻,只处理部分的状态。

四、算法实现框架本次实验使用的语言是java,具体的算法实现包含4个类:ViterbiDecode、StateNode、ConvEncode和Channel。

ViterbiDecode类用于实现维特比译码,它有一个静态的decode()方法用于译码和一个静态的hammingDistance()方法用于计算汉明距离,ViterbiDecode的main方法是程序的入口;StateNode是一个实体类,它用于保存状态,有value(状态,如“00”)、distance(汉明距离)和solution(对应译码序列)属性;ConvEncode类用于实现(2,1,3)卷积码编码,它只有一个静态的encode()方法用于编码和一个静态的addZero()方法用于补0操作;Channel类用于模拟有噪信道,它只有一个静态的transfer()方法用于给比特流序列加上误码。

五、总结本科的时候曾经学习过卷积码编码和维特比译码的知识,考研的时候也复习了该方面知识,在理解题目上没有遇到困难。

但由于从未尝试编程实现维特比译码,因此在本次实验的过程中还是遇到了许多问题。

经过查看课件及网上查找资料,我对如何编码实现卷积码的维特比译码算法有了一个较好的理解,知道了算法主要应该包括补0、卷积编码、信道传输和维特比译码4步操作,并使用了ja va语言自主完成了该实验。

由于该实验完成的比较仓促,程序中仍有许多地方能进行优化,但总的来说,通过本次试验,我对维特比译码有了一个直观的理解,同时锻炼了使用java语言编程的能力,有不小的收获。

附录程序运行示例:源程序:import java.util.Random;import java.util.Scanner;publicclass ViterbiDecode{publicstatic byte[] decode(byte[] inBytes) {StateNode[] stateNodes = new StateNode[4];StateNode[]tmpNodes = new StateNode[4];stateNodes[0] = newStateNode("00", 0, "");ﻩﻩfor (int i = 0;i< inBytes.length - 4; i = i + 2) {byte[] twoBytes = { inBytes[i], inBytes[i + 1] };ﻩfor (int j = 0; j < stateNodes.length; j++) {ﻩﻩif (stateNodes[j] != null) {ﻩﻩﻩString value = stateNodes[j].value;ﻩﻩﻩbyte[] byteValue = {ﻩﻩﻩByte.parseByte(value.substring(0,ﻩﻩﻩﻩﻩﻩvalue.length() - 1)),ﻩﻩﻩﻩByte.parseByte(value.substring(value.length()-1)) };ﻩﻩbyte[][] possibleNextValue = {{0, byteValue[0] },ﻩﻩﻩ{ 1, byteValue[0] } };ﻩﻩﻩ// String[] possibleNextOutput ={};ﻩﻩﻩbyte[][] possibleNextOutput = {ﻩﻩﻩﻩﻩ{ﻩﻩﻩ(byte) ((byteValue[0] +byteValue[1] + possib leNextValue[0][0]) % 2),ﻩﻩ(byte) ((byteValue[1] + possibleNextValue[0][0]) % 2) },ﻩﻩﻩﻩ{ﻩﻩﻩﻩ(byte) ((byteValue[0] + byteValue[1] + possibleNextVa lue[1][0]) % 2),ﻩﻩﻩﻩ(byte) ((byteValue[1] + possibleNextValue[1][0]) % 2) } };ﻩﻩﻩint[] distances ={ﻩﻩﻩstateNodes[j].distanceﻩﻩﻩﻩﻩﻩ+ViterbiDecode.hammingDistance(twoBytes,ﻩﻩﻩﻩpossibleNextOutput[0]),ﻩﻩﻩstateNodes[j].distanceﻩﻩﻩ+ ViterbiDecode.hammingDistance(twoBytes,ﻩﻩﻩpossibleNextOutput[1]) };ﻩﻩﻩﻩfor (int k = 0; k < possibleNextValue.length; k++) {ﻩﻩbyte[] next = possibleNextValue[k];ﻩﻩﻩﻩStateNode tmpNode = tmpNodes[next[1] + 2 * next[0]];ﻩﻩﻩﻩif (tmpNode != null) {ﻩﻩint d = tmpNode.distance;ﻩﻩﻩﻩif (distances[k]< d){ﻩﻩﻩﻩtmpNode.distance =distances[k];ﻩﻩﻩﻩﻩtmpNode.solution = stateNodes[j].solutionﻩﻩﻩﻩ+possibleNextValue[k][0];ﻩﻩﻩ}ﻩﻩﻩ} else {ﻩﻩﻩﻩtmpNodes[next[1] + 2 * next[0]] = new StateNode(ﻩﻩﻩﻩﻩﻩnext[0] + ""+ next[1], distances[k],ﻩﻩﻩﻩﻩstateNodes[j].solutionﻩﻩﻩﻩﻩﻩﻩﻩ+ possibleNextValue[k][0]);ﻩﻩﻩﻩﻩ}ﻩﻩﻩ}ﻩﻩ}ﻩ}ﻩfor (int m = 0; m < tmpNodes.length; m++) {ﻩstateNodes[m] = tmpNodes[m];ﻩﻩﻩtmpNodes[m] = null;ﻩ}}ﻩfor (int i = inBytes.length - 4; i < inBytes.length; i = i+2) { ﻩﻩbyte[] twoBytes = { inBytes[i], inBytes[i+ 1] };ﻩfor (int j = 0; j < stateNodes.length; j++) {if (stateNodes[j] != null) {ﻩString value = stateNodes[j].value;ﻩﻩﻩbyte[] byteValue = {ﻩﻩﻩﻩﻩByte.parseByte(value.substring(0,ﻩﻩﻩvalue.length() - 1)),ﻩﻩﻩﻩByte.parseByte(value.substring(value.length() - 1)) }; ﻩﻩﻩﻩbyte[] possibleNextValue = { 0, byteValue[0] };ﻩﻩbyte[]possibleNextOutput = {ﻩﻩﻩﻩﻩ(byte) ((byteValue[0] + byteValue[1]) % 2),ﻩﻩﻩﻩ(byte) ((byteValue[1]) % 2) };ﻩﻩﻩﻩint distance =stateNodes[j].distanceﻩﻩﻩﻩ+ViterbiDecode.hammingDistance(twoBytes,ﻩﻩﻩﻩﻩpossibleNextOutput);ﻩﻩﻩﻩStateNodetmpNode = tmpNodes[possibleNextValue[1]+2ﻩﻩﻩ* possibleNextValue[0]];ﻩﻩif (tmpNode!= null) {ﻩﻩﻩﻩint d = tmpNode.distance;ﻩﻩﻩﻩif (distance < d) {ﻩtmpNode.distance = distance;ﻩﻩﻩtmpNode.solution = stateNodes[j].solutionﻩﻩﻩﻩ+ possibleNextValue[0];ﻩﻩﻩ}ﻩﻩﻩ} else {ﻩﻩﻩtmpNodes[possibleNextValue[1]+2ﻩﻩﻩ* possibleNextValue[0]] = new StateNode( ﻩﻩﻩpossibleNextValue[0] + ""ﻩﻩﻩﻩ+ possibleNextValue[1], distance,ﻩﻩﻩﻩstateNodes[j].solution + possibleNextValue[0]);ﻩﻩﻩﻩ}}ﻩ}ﻩfor(int m = 0;m <tmpNodes.length; m++) {ﻩﻩﻩstateNodes[m] = tmpNodes[m];ﻩtmpNodes[m] = null;ﻩ}ﻩ}byte[] outBytes =newbyte[stateNodes[0].solution.length()];ﻩﻩfor (int i = 0; i < outBytes.length; i++) {ﻩﻩoutBytes[i] = Byte.parseByte(stateNodes[0].solution.substring(i,ﻩﻩﻩﻩi + 1));}ﻩﻩreturn outBytes;}ﻩ// a和b都是0 1,且a b等长ﻩpublic static int hammingDistance(byte[] a, byte[] b) {ﻩint distance =0;ﻩﻩfor (int i = 0; i < a.length; i++) {ﻩﻩif (a[i] != b[i])ﻩdistance++;ﻩ}ﻩreturn distance;ﻩ}ﻩpublicstatic void main(String[]args) {ﻩ// 输入参数ﻩScanner in=newScanner(System.in);ﻩﻩwhile (true) {ﻩﻩﻩSystem.outﻩﻩ.println("********模拟实现比特流的(2,1,3)卷积编码->经过有噪信道->维特比译码********");ﻩSystem.out.println("请输入比特流的长度并回车:");int n = in.nextInt();ﻩﻩﻩSystem.out.println("请输入比特流(0或者1,空格隔开)并回车:");ﻩﻩbyte[] a = new byte[n];ﻩfor (int i = 0; i < a.length; i++) {ﻩﻩa[i] = in.nextByte();}ﻩﻩﻩSystem.out.println("请输入有噪信道误码率(0~1)并回车:");ﻩﻩﻩdouble r= in.nextDouble();ﻩﻩ//记录信道传输造成的误码个数err1和维特比译码还原结果的误码个数err2ﻩﻩint err1 = 0;ﻩint err2 = 0;ﻩ// 输出原始比特流数据ﻩﻩﻩSystem.out.println("原始比特流数据:");ﻩﻩfor (int i = 0; i < a.length; i++) {ﻩﻩSystem.out.print(a[i] + "");ﻩ}System.out.println("");ﻩﻩﻩ// 输出卷及编码结果ﻩﻩﻩSystem.out.println("经过(2,1,3)卷积编码后的比特流数据:");ﻩﻩbyte[] b = ConvEncode.encode(a);ﻩfor (int i = 0; i < b.length; i++) {ﻩSystem.out.print(b[i] + " ");}ﻩﻩSystem.out.println("");ﻩﻩﻩ// 输出信道传输结果System.out.println("经过误码率为" + r + "的有噪信道后的比特流数据:");byte[] c= Channel.transfer(b, r);ﻩﻩfor (int i = 0;i < c.length; i++) {ﻩSystem.out.print(c[i] + " ");ﻩif(c[i] != b[i]) {ﻩﻩﻩﻩﻩerr1++;ﻩﻩ}ﻩ}ﻩﻩﻩSystem.out.println("");ﻩﻩ//输出有噪信道造成的误码个数ﻩSystem.out.println("经过有噪信道后造成的误码个数是" + err1);ﻩ//输出维特比译码结果System.out.println("经过维特比译码还原的比特流数据:");ﻩbyte[] d = ViterbiDecode.decode(c);ﻩfor (int i=0; i < d.length - 2; i++) {ﻩﻩSystem.out.print(d[i] + " ");ﻩif(d[i] != a[i]) {ﻩerr2++;ﻩﻩ}}ﻩSystem.out.println("");ﻩ//输出维特比译码还原结果的误码个数ﻩSystem.out.println("经过维特比译码还原后,与原始比特流数据相比的误码个数是" + err2);ﻩﻩSystem.out.println("");ﻩ}ﻩ}}class StateNode {String value;ﻩint distance;String solution;public StateNode(String value, int distance, String solution) {ﻩthis.value = value;ﻩﻩthis.distance = distance;ﻩthis.solution = solution;}}// 模拟实现(2,1,3)卷积码class ConvEncode {ﻩ// 输入只能是0 1,编码器的顺序是输入->s1->s2->丢弃public staticbyte[] encode(byte[] inBytes) {ﻩbyte[] actualBytes = ConvEncode.addZero(inBytes);ﻩbyte[] outBytes= new byte[2 * actualBytes.length];ﻩbytes1 = 0;ﻩﻩbyte s2 =0;ﻩfor (inti=0; i < actualBytes.length; i++){ﻩﻩoutBytes[2 * i] = (byte) ((s1 + s2 +actualBytes[i]) % 2 == 0 ? 0ﻩ: 1);ﻩﻩoutBytes[2 * i+1] = (byte) ((s2 +actualBytes[i]) % 2== 0?0ﻩﻩﻩﻩ: 1);ﻩﻩs2 = s1;ﻩﻩs1 = actualBytes[i];ﻩ}return outBytes;}ﻩprivate static byte[] addZero(byte[] inBytes) {--ﻩﻩbyte[] outBytes = new byte[inBytes.length +2];ﻩfor (int i = 0; i < outBytes.length - 2; i++) {ﻩoutBytes[i] = inBytes[i];}ﻩoutBytes[outBytes.length - 2] = 0;ﻩﻩoutBytes[outBytes.length- 1] =0;return outBytes;ﻩ}}//模拟传输信道,为比特增加误码class Channel {// 输入只能是01,errorRate取值0(包含)~1(不包含)ﻩpublic static byte[] transfer(byte[] inBytes, double errorRate) {ﻩﻩbyte[] outBytes =new byte[inBytes.length];ﻩRandom r = new Random();ﻩdouble randomDouble;ﻩfor (int i =0; i < outBytes.length; i++) {outBytes[i]= inBytes[i];randomDouble= r.nextDouble();ﻩif (randomDouble < errorRate) {ﻩﻩﻩoutBytes[i] = (byte) (outBytes[i] ==0 ? 1 : 0);ﻩﻩ}ﻩ}return outBytes;ﻩ}}--。

相关主题