
简介这份文档是《信息论与编码》曹雪虹教材的课后习题参考答案面向正在学习信息论基础课程的高校学生与备考人员帮助解决课后练习无从下手的困扰。压缩包内仅含1个doc文件大小约695KB内容按章节整理覆盖马尔科夫链、状态图、信息熵、自信息量、条件熵等核心概念并给出转移概率、状态转移矩阵、信息压缩与加密等延伸知识点的解题过程。从预览可见答案包含马尔可夫信源稳态概率推导、掷骰子事件自信息量计算、离散无记忆信源序列信息量求解、四进制与八进制脉冲信息量比较等典型题型步骤较为完整便于对照教材逐题核对。目前已有2964人学习下载适合作为课后自测、期末复习与概念梳理的参考材料。1. 信息论与编码课后习题参考答案从“对答案”到“真会算”的分水岭很多人第一次翻开信息论与编码的教材看到熵、互信息、信道容量、汉明码这些词第一反应是“公式我认识题我不会做”。尤其是曹雪虹老师这本教材课后习题覆盖面广从信源熵计算到香农编码、费诺编码、哈夫曼编码再到循环码和卷积码每一章都有大量需要动手推导的题目。于是“信息论与编码-曹雪虹-课后习题参考答案.doc”成了很多人搜索的关键词——大家想要的不是一份抄完就忘的答案而是一条能让自己独立把题算对的路径。这份参考答案真正的价值不在于给出最终数字而在于展示中间步骤概率怎么归一化、联合熵怎么拆、信道矩阵怎么列、生成多项式怎么选。如果你只是把答案抄一遍考试换个数就翻车但如果你把每道题的推导逻辑吃透信息论与编码这门课反而会成为你后续做通信、压缩、纠错的地基。这篇文章面向正在学这门课、或者需要快速复习信息论核心计算的人从“怎么用参考答案”讲到“怎么自己算对”再到“哪些坑最容易踩”。2. 信源熵与互信息参考答案里最容易被跳过的计算细节2.1 先分清自信息、熵、联合熵、条件熵的适用场景很多课后题第一眼看过去是在求“熵”但实际要区分四种量自信息、信源熵、联合熵、条件熵。参考答案里通常直接写结果但如果你不清楚每个量的定义域换一道题就会混。自信息是对单个事件而言单位是比特信源熵是对整个概率分布求期望联合熵针对两个随机变量的联合分布条件熵则是在已知一个变量后另一个变量还剩多少不确定性。我一般会先画一张表把题目给的联合概率分布列成矩阵行表示X列表示Y然后逐行逐列求和得到边缘概率。这一步不做后面所有计算都是空中楼阁。参考答案里经常省略边缘概率的推导直接代入公式但恰恰是这一步决定了你后面会不会把行列搞反。量公式单位常见误用自信息I(x) -log₂P(x)比特把概率为0的事件代入信源熵H(X) -ΣP(x)log₂P(x)比特/符号忘记归一化概率联合熵H(X,Y) -ΣΣP(x,y)log₂P(x,y)比特/符号对行列求和顺序错条件熵H(YX) ΣP(x)H(YXx)注意概率为0的项在熵计算中贡献为0不要因为log₂0无定义就跳过整道题按极限处理即可。2.2 用Python验证课后题里的熵计算参考答案里的数字不一定错但你最好自己写几行代码验证。下面这段代码以一道典型的联合概率分布题为例计算边缘熵、联合熵、条件熵和互信息。你只需要把题目给的联合概率矩阵替换进去就能快速核对答案。import numpy as np # 联合概率分布 P(X,Y)行是X列是Y # 这里用一个典型课后题分布作为示例 Pxy np.array([ [1/8, 1/16, 1/32, 1/32], [1/16, 1/8, 1/32, 1/32], [1/16, 1/16, 1/16, 1/16], [1/4, 0, 0, 0] ]) # 归一化检查防止题目数据录入错误 Pxy Pxy / Pxy.sum() print(归一化后总和:, Pxy.sum()) # 边缘概率 Px Pxy.sum(axis1) # 对Y求和得到X的边缘 Py Pxy.sum(axis0) # 对X求和得到Y的边缘 # 熵函数处理0概率 def entropy(p): p p[p 0] return -np.sum(p * np.log2(p)) H_X entropy(Px) H_Y entropy(Py) H_XY entropy(Pxy.flatten()) # 条件熵 H(Y|X) H(X,Y) - H(X) H_Y_given_X H_XY - H_X H_X_given_Y H_XY - H_Y # 互信息 I(X;Y) H(X) H(Y) - H(X,Y) I_XY H_X H_Y - H_XY print(fH(X) {H_X:.4f} bit) print(fH(Y) {H_Y:.4f} bit) print(fH(X,Y) {H_XY:.4f} bit) print(fH(Y|X) {H_Y_given_X:.4f} bit) print(fH(X|Y) {H_X_given_Y:.4f} bit) print(fI(X;Y) {I_XY:.4f} bit)这段代码的关键在于entropy函数里先过滤掉零概率项避免log2(0)报错。Pxy.sum(axis1)得到的是行和对应X的边缘分布axis0得到列和对应Y的边缘分布。条件熵用H(X,Y)-H(X)计算比直接套条件概率公式更不容易出错。互信息用H(X)H(Y)-H(X,Y)验证结果应该和H(Y)-H(Y|X)一致。如果你算出来的互信息是负数那一定是边缘概率或联合概率搞反了。2.3 参考答案里条件熵的两种算法怎么选课后题里求条件熵通常有两种路径一种是先求条件概率P(y|x)再对每个x求条件熵后加权另一种是用H(X,Y)-H(X)。参考答案往往只写一种但你自己要会两种因为题目给的数据形式不同。如果题目直接给了联合概率矩阵用减法更快如果题目给了条件概率和边缘概率那就直接加权求和。我见过不少同学在考试时用减法结果把H(X,Y)-H(Y)当成H(Y|X)整道题白算。记住H(Y|X)H(X,Y)-H(X)减掉的是条件里那个已知变量的熵。这个细节在参考答案里不会特意提醒但它是血泪教训。3. 信道容量与编码定理从参考答案反推参数设置3.1 信道矩阵的列归一化与对称信道判定信道容量这一章的课后题核心是给一个信道转移矩阵求容量。参考答案通常会写“该信道为对称信道容量为Clog₂M-H(行向量)”。但问题在于很多题目给的不是标准对称信道你需要先判断它是否对称再决定用哪个公式。判断对称信道的条件是转移矩阵的每一行都是同一组元素的排列每一列也是同一组元素的排列。如果只满足行排列叫行对称只满足列排列叫列对称。强对称信道要求行列都对称。参考答案里经常直接说“由对称性可知”但如果你没验证就套公式遇到非对称信道就会翻车。我一般会先做列归一化检查每一列的和应该等于1因为给定输入后输出概率之和为1。如果列和不为1说明矩阵抄错了或者题目给的是非归一化形式。然后检查行向量是否互为排列列向量是否互为排列。import numpy as np # 信道转移矩阵行是输入列是输出 P np.array([ [0.7, 0.2, 0.1], [0.2, 0.7, 0.1], [0.1, 0.2, 0.7] ]) # 列归一化检查 col_sums P.sum(axis0) print(列和:, col_sums) assert np.allclose(col_sums, 1.0), 列和不为1矩阵有误 # 行和检查 row_sums P.sum(axis1) print(行和:, row_sums) # 判断是否强对称每行是否为同一组元素的排列 def is_permutation_rows(mat): sorted_rows np.sort(mat, axis1) return np.allclose(sorted_rows, sorted_rows[0]) def is_permutation_cols(mat): sorted_cols np.sort(mat, axis0) return np.allclose(sorted_cols, sorted_cols[:, 0]) print(行对称:, is_permutation_rows(P)) print(列对称:, is_permutation_cols(P)) # 对称信道容量 C log2(M) - H(行向量) M P.shape[1] row_entropy -np.sum(P[0] * np.log2(P[0])) C np.log2(M) - row_entropy print(f信道容量 C {C:.4f} bit/符号)这段代码先验证列和再判断行列对称性。is_permutation_rows把每一行排序后比较如果所有行排序后相同说明行之间是排列关系。对称信道容量公式Clog₂M-H(行向量)只适用于强对称信道其中M是输出符号数。如果信道不是对称的需要用Blahut-Arimoto算法迭代求容量课后题一般不会要求手算到那一步但你要知道公式的适用边界。3.2 香农编码、费诺编码、哈夫曼编码的手算步骤对比编码这一块的课后题参考答案往往只给最终码字但考试要求你写出完整过程。三种编码方法的手算步骤差异很大我整理成表格方便对照。编码方法核心步骤是否唯一平均码长特点香农编码按概率降序累加概率转二进制取位唯一接近但通常大于H(X)费诺编码按概率分组使两组概率和尽量接近不唯一接近H(X)哈夫曼编码每次合并最小两个概率反向分配0/1不唯一最优等于或接近H(X)香农编码的关键是累加概率的二进制表示取前ceil(-log₂P(x))位。费诺编码的关键是每次分组要尽量均分分组方式不同会导致码长不同。哈夫曼编码从叶子反向建树合并顺序决定码字但不影响平均码长。我一般会先用哈夫曼编码求最优平均码长再用香农编码验证是否满足H(X)≤LH(X)1。如果参考答案给的香农编码平均码长比哈夫曼还短那一定是算错了因为哈夫曼是最优的。3.3 用代码复现哈夫曼编码并核对参考答案手算哈夫曼编码容易在合并顺序上出错尤其是概率相同时。下面这段代码实现哈夫曼编码并输出每个符号的码字和平均码长你可以直接和参考答案对比。import heapq def huffman_coding(symbols, probs): # 构建优先队列元素为(概率, 唯一标识, 节点) heap [[p, i, {s: }] for i, (s, p) in enumerate(zip(symbols, probs))] heapq.heapify(heap) while len(heap) 1: # 弹出两个最小概率节点 lo heapq.heappop(heap) hi heapq.heappop(heap) # 合并左分支加0右分支加1 for s in lo[2]: lo[2][s] 0 lo[2][s] for s in hi[2]: hi[2][s] 1 hi[2][s] # 新节点概率为两者之和 merged {**lo[2], **hi[2]} heapq.heappush(heap, [lo[0] hi[0], lo[1], merged]) return heap[0][2] symbols [a, b, c, d, e] probs [0.4, 0.2, 0.2, 0.1, 0.1] codes huffman_coding(symbols, probs) avg_len sum(probs[i] * len(codes[s]) for i, s in enumerate(symbols)) print(符号\t概率\t码字\t码长) for i, s in enumerate(symbols): print(f{s}\t{probs[i]}\t{codes[s]}\t{len(codes[s])}) print(f平均码长 L {avg_len:.4f} bit)代码里用heapq维护最小堆每次弹出两个最小概率节点合并。lo[2]和hi[2]是字典存储该节点下所有符号的当前码字。合并时左分支加0、右分支加1这个约定和参考答案可能相反但平均码长不变。avg_len用概率加权码长得到。如果参考答案的平均码长和这里不一致先检查概率是否归一化再检查合并顺序是否影响了码长分配。4. 线性分组码与循环码生成矩阵、校验矩阵的避坑指南4.1 生成矩阵的系统化与校验矩阵的对应关系线性分组码的课后题通常给生成矩阵G要求求校验矩阵H、最小距离、纠错能力。参考答案里经常直接写H但中间的系统化步骤省略了。如果你不把G化成系统形式[I|P]直接写H很容易把P的位置搞反。系统化生成矩阵的形式是G[I_k|P]其中I_k是k阶单位阵P是k×(n-k)矩阵。对应的校验矩阵是H[P^T|I_{n-k}]。注意P^T是P的转置I_{n-k}是n-k阶单位阵。参考答案里如果H和G不是这个关系那要么G没系统化要么H写错了。我一般会先对G做行变换把它变成系统形式。行变换包括交换两行、把某行加到另一行、某行乘以非零常数。这些操作不改变码空间但会改变G的表示。系统化之后P矩阵一目了然H也就直接写出来了。4.2 循环码的生成多项式与伴随式计算循环码的课后题核心是生成多项式g(x)。参考答案通常给一个g(x)然后要求求生成矩阵、校验矩阵、编码电路。但g(x)必须满足两个条件它是x^n-1的因式且次数为n-k。如果题目给的g(x)不满足这两个条件那这道题本身有问题。伴随式译码是循环码的另一个重点。收到码字r(x)后计算伴随式s(x)r(x) mod g(x)。如果s(x)0说明没有错误否则根据s(x)查表找错误图样。参考答案里经常只给伴随式结果不展示多项式除法过程。我建议手算时用长除法把每一步写清楚这样考试时不会因为跳步丢分。import numpy as np def poly_mod(dividend, divisor): 多项式模2除法返回余数 dividend dividend.copy() divisor divisor.copy() while len(dividend) len(divisor): if dividend[0] 1: for i in range(len(divisor)): dividend[i] ^ divisor[i] dividend dividend[1:] return dividend # 示例g(x) x^3 x 1对应系数[1,0,1,1] g np.array([1, 0, 1, 1]) # 收到码字 r(x) x^6 x^5 x^2 1对应[1,1,0,0,1,0,1] r np.array([1, 1, 0, 0, 1, 0, 1]) syndrome poly_mod(r, g) print(伴随式系数:, syndrome) if np.all(syndrome 0): print(无错误) else: print(有错误需查表译码)这段代码实现模2多项式除法。poly_mod函数每次看被除数的最高位如果是1就异或除数然后右移一位。最终剩下的就是余数即伴随式。注意多项式的系数顺序是从高次到低次g[1,0,1,1]对应x^30x^21x1。如果伴随式全零说明接收码字是合法码字否则需要根据伴随式查错误图样表。4.3 最小距离与纠错能力的快速判定线性分组码的最小距离d_min等于非零码字的最小重量。参考答案里经常直接给d_min但你要会自己验证。对于系统码d_min等于G中非零行的最小重量但这不是充分条件因为行组合可能产生更小重量。最可靠的方法是枚举所有2^k个码字找最小非零重量。课后题k一般不大枚举可行。纠错能力tfloor((d_min-1)/2)。如果d_min3可以纠1位错d_min4可以纠1位错并检2位错d_min5可以纠2位错。参考答案里如果写“可纠2位错”但d_min4那是不对的因为4只能保证纠1位。5. 卷积码与维特比译码状态图、网格图的手算技巧5.1 卷积码编码器的状态转移与输出计算卷积码的课后题通常给一个编码器结构要求画状态图、网格图或者用维特比译码。参考答案里状态图往往画得很简略但如果你不自己推一遍状态转移考试时根本画不出来。我一般会先确定约束长度和状态数。如果编码器有m级移位寄存器状态数就是2^m。然后列出当前状态和输入下的下一状态及输出。以(2,1,3)卷积码为例m2状态数4。输入1位输出2位。状态转移表可以手算也可以用代码生成。def conv_encoder(input_bits, g1, g2, m): 卷积码编码器g1和g2是生成多项式系数 state [0] * m output [] for bit in input_bits: # 当前输入加上移位寄存器内容 reg [bit] state # 模2和 out1 sum(reg[i] * g1[i] for i in range(len(g1))) % 2 out2 sum(reg[i] * g2[i] for i in range(len(g2))) % 2 output.extend([out1, out2]) # 更新状态 state [bit] state[:-1] return output # 示例g1111, g2101, m2 input_bits [1, 0, 1, 1] g1 [1, 1, 1] g2 [1, 0, 1] encoded conv_encoder(input_bits, g1, g2, 2) print(编码输出:, encoded)代码里reg是当前输入位加上移位寄存器状态g1和g2分别对应两个输出支路的抽头系数。sum(...)%2实现模2和。每编码一位状态右移并填入新输入。输出顺序是每输入一位输出两位所以最终输出长度是输入长度的两倍。你可以用这段代码验证参考答案里的编码结果。5.2 维特比译码的路径度量与回溯维特比译码是卷积码课后题的重头戏。参考答案通常给一个接收序列要求找最大似然路径。手算时容易在路径度量累加和回溯上出错。我一般会画网格图每个时刻每个状态标出累积度量然后从最后一个时刻的最小度量状态回溯。关键点路径度量是汉明距离的累加不是欧氏距离。每到一个状态比较两条到达路径的累积度量保留小的那条。如果两条相等可以任选一条但参考答案可能只给一种。回溯时从最终状态沿幸存路径反向走到起点得到译码输出。注意维特比译码的复杂度随状态数指数增长课后题状态数一般不超过8手算可行。如果状态数超过16建议写代码。6. 用参考答案的常见翻车现场与排查清单6.1 概率未归一化导致熵计算全错现象算出来的熵大于log₂N或者互信息为负。 原因题目给的联合概率矩阵没有归一化或者你抄错了某个概率值。 解决第一步永远是对概率矩阵求和如果不是1先归一化再算。参考答案里的概率通常已经归一化但你自己录入代码时要检查。6.2 信道矩阵行列搞反导致容量公式用错现象套对称信道容量公式算出的C和参考答案差很多。 原因把行当成输出、列当成输入或者判断对称性时只看行不看列。 解决记住信道矩阵的行对应输入、列对应输出。列和必须为1。判断对称性时行列都要检查。非对称信道不能用log₂M-H(行)。6.3 哈夫曼编码合并顺序不同导致码字不一致现象你的码字和参考答案不一样但平均码长相同。 原因哈夫曼编码不唯一概率相同时合并顺序不同会产生不同码字。 解决只要平均码长一致码字不同不算错。但如果平均码长不同检查是否某次合并没选最小的两个概率。6.4 循环码生成多项式次数不对导致矩阵维度错现象生成的矩阵行数或列数和参考答案不一致。 原因g(x)的次数不等于n-k或者g(x)不是x^n-1的因式。 解决先验证g(x)的次数再验证它能否整除x^n-1。如果题目给的g(x)不满足那题目本身有问题按题目要求做即可但心里要知道边界。6.5 维特比译码回溯路径选错导致译码输出不同现象你的译码输出和参考答案不同但路径度量相同。 原因两条路径累积度量相等时参考答案选了另一条。 解决维特比译码在度量相等时输出不唯一只要你的路径度量是最小的就算正确。考试时如果参考答案只给一种可以备注“另一条等度量路径也可”。7. 把参考答案变成自己的验算工具一个可复用的检查习惯我后来养成了一个习惯每做完一道信息论与编码的课后题不急着对参考答案而是先用自己的代码或手算表格验证三个东西——概率是否归一化、熵是否在合理范围、码长是否满足编码定理的下界。这个习惯让我在考试时少丢了很多冤枉分。具体来说熵计算后检查0≤H(X)≤log₂N互信息检查0≤I(X;Y)≤min(H(X),H(Y))信道容量检查0≤C≤log₂M平均码长检查H(X)≤LH(X)1。这些不等式就像后悔药一旦越界不用看参考答案就知道自己错了。对于循环码和卷积码我会用Python把生成矩阵、校验矩阵、状态转移表都打印出来和手算结果逐行对比。代码不会骗人如果代码和手算不一致那一定是手算某一步漏了或抄错了。这个验算过程比单纯抄答案慢但做上十道题之后速度会反过来超过抄答案因为你不必反复翻参考答案确认。最后一个技巧把参考答案里的最终数字遮住只看题目自己完整算一遍然后拿代码验证。如果代码结果和参考答案一致说明你算对了如果不一致先查代码再查手算最后才怀疑参考答案。多数情况下参考答案是对的但偶尔也会有印刷错误这时候你的验算能力就是最后的防线。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。