用Python复算信息论课后题:熵与信道容量计算要点 📅 发布时间:2026/9/18 23:00:16 👁 浏览次数: 简介一份覆盖《信息论与编码》核心章节的课后习题参考答案围绕离散信源、信息量、熵、无记忆信源等基础知识展开面向电子信息、通信工程及相关专业学生适用于课后自测、期末复习与考研基础梳理。资料以 PDF 文档形式提供共 1 个文件大小约 2.16MB结构清晰便于按题号顺序查阅。目前已有 2313 人学习下载是课程复习中较受欢迎的配套材料。答案对掷骰子组合的自信息量与熵、棋盘质点落入方格的平均信息量、色盲调查回答的信息量、二元无记忆信源序列熵等典型题目给出逐步推导与数值结果并对信源熵极值性等易混淆概念作了说明帮助读者理解公式的适用条件、掌握计算细节。资料虽以课后题解析为主但题目背景多样也可作为梳理信息论基本概念的辅助参考。1. 一份课后答案的价值把“自信息量-熵-信道容量”当成可计算管线很多通信和计算机方向的开发者第一次系统性接触信息论不是从教材定理开始而是从一份《信息论与编码课后习题参考答案.pdf》开始的。这份资料里有大量“给定分布求熵、给定信道矩阵求容量”的原题1.1的掷骰子、1.4的二元无记忆信源、2.13的BSC串接信道几乎把香农信息度量从头到尾过了一遍。对要复习考研、期末考或准备面试的人来说真正值钱的不是答案本身而是每一道题都对应一条可以程序化的计算流水线先定概率空间再算信息量最后看信道矩阵的结构选容量公式。这篇文章就按这个思路把题拆开顺便说说哪些数字不能直接抄。2. 自信息量与熵从掷骰子例题建立信源度量骨架信息论里最容易被低估的公式是I(x) -log₂P(x)。它看起来简单但绝大多数计算错误都出在概率没有数清楚。以1.1题为例同时掷一对均匀骰子时“2和6同时出现”这个事件包含两个有序样本点2,6和6,2所以概率是2/36而不是1/36对应的自信息量是log₂18≈4.17bit。而“两个5同时出现”只包含5,5一个样本点概率1/36信息量log₂36≈5.17bit。两个事件相差1bit差距完全来自样本点数。做工程时如果按直观“某个点数出现的次数”去估概率这种1bit的偏差就会直接影响后面熵和码率估算。2.1 组合信源与点数和信源先分清样本空间再算熵1.1的第三问和第四问是很好的对照。组合信源的符号是“两个骰子的无序组合”比如(1,2)和(2,1)算同一个组合所以21个符号中6个“对子”概率1/3615个“不同组合”概率2/36。其熵为H(组合) -6*(1/36)log₂(1/36) - 15*(2/36)log₂(2/36) ≈ 4.34 bit/symbol点数和信源有11个符号概率呈三角分布2和12各1/363和11各2/36……7为6/36。按熵公式算出来约3.27bit而不是部分答案版本里的3.71。这个0.4bit的差距在考试里可能只扣一分但如果把它当作传感器信源每秒产生的比特数一天下来误差超过三万bit所以值得用程序校准。2.2 用Python验证掷骰子熵计算下面这段代码把上面的计算固化成一个可复用的熵函数之后所有题都用它。import math def entropy(probs): # 传入概率列表p0的项不参与计算 return -sum(p * math.log2(p) for p in probs if p 0) # 两个骰子无序组合6个相同点数(1/36) 15个不同点数组合(2/36) comb [1 / 36] * 6 [2 / 36] * 15 print(组合熵:, round(entropy(comb), 4)) # 约4.3366 bit # 两个骰子点数之和概率按2~12排列 sum_prob [i / 36 for i in [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]] print(点数和熵:, round(entropy(sum_prob), 4)) # 约3.2744 bitentropy函数用生成器跳过0概率避免log(0)报错comb列表的构造是关键15是C(6,2)的非对子组合数。对照一下如果直接把36个有序样本点都当成符号熵是log₂365.17bit那不是本题问的“组合熵”。很多复算者在这里多乘了2得到高于4.34的结果原因就是把有序对和无序组合混在一个概率空间里。点数和熵同理每一项概率由组合计数决定不能直接取等概率。信源符号数概率结构熵bit有序点对36全等概率1/365.1699无序组合216个1/3615个2/364.3366点数和11三角分布3.27442.3 1.5题的反常结果先检查概率归一化1.5题给了一个6符号信源参考答案算出H(X)≈2.725bit同时log₂6≈2.585bit于是出现“H(X)log6不满足极值性”的结论。这个结论在数学上不可能成立离散均匀分布是唯一的最大熵分布最大熵就是log n任何非均匀分布的熵都严格小于log n。所以当你看到H(X)log n第一反应应该是概率没有归一化或对数底混用。我对照过不同扫描版本这题的符号概率经常被复制成0.3、0.18、0.16、0.18、0.19、0.17六个加起来是1.18显然不是合法分布。正确做法是先看给定概率是否和为1不全则检查是否漏了条件概率或者题目本身给的是条件分布。这个检查应该成为所有信息论计算的第一步之后再看熵。3. 多符号信源与联合熵拆开1.4和2.3的计算链条单符号熵是静态的真实信源传感器报文、编码后的比特流往往是长序列这时要处理“多个随机变量”的联合分布。1.4题是长度1000的二元无记忆信源2.3题是一个三维联合分布两题合起来就是理解“序列熵”和“互信息”的入口。3.1 二元无记忆信源特定序列的自信息量是线性函数1.4题中P(0)1/3、P(1)2/3序列长1000。设某个特定序列里0出现m次、1出现1000-m次那么P(x) (1/3)^m * (2/3)^(1000-m)I(x) -log₂P(x) m*log₂3 (1000-m)*log₂(3/2) bit这里的m从0到1000每个m对应的序列数量是C(1000,m)但单个特定序列的自信息量只与m有关不是与哪个位置有关所以I是m的线性函数。这正是“无记忆”的体现概率连乘取对数后变成求和。序列熵则是所有序列自信息量的期望由于信源无记忆直接等于1000倍单符号熵约918.9bit。很多初学者把“特定序列的自信息量”和“序列熵”混为一谈前者依赖m后者是平均值。3.2 2.3题的三维联合熵先建分布再边缘化2.3题的输入是(X,Y)联合概率P(0,0)1/8P(0,1)3/8P(1,0)3/8P(1,1)1/8。X和Y的边缘分布都是等概所以H(X)H(Y)1bitI(X;Y)0。接着定义ZXY这里Z是逻辑与Z1当且仅当X1且Y1因此H(Z)0.544bit。如果要算H(XZ)不能直接从H(X)和H(Z)相加因为X和Z不独立。正确做法是先构造三元联合分布再边缘化到(X,Z)。这个问题在试卷上要手算在工程里就是几行代码的事。先建p_xyz用字典保存每个(x,y,z)的概率然后写一个边缘化函数按要保留的维度聚合并重新归一化最后复用entropy函数。3.3 Python任意联合分布的统一熵计算import math def entropy(probs): return -sum(p * math.log2(p) for p in probs if p 0) def marginalize(dist, keep): out {} for key, p in dist.items(): new_key tuple(key[i] for i in keep) out[new_key] out.get(new_key, 0.0) p return out # 2.3 题给的 (X,Y) 联合概率 p_xy {(0, 0): 1/8, (0, 1): 3/8, (1, 0): 3/8, (1, 1): 1/8} p_xyz {} for (x, y), pxy in p_xy.items(): z 1 if x 1 and y 1 else 0 # Z XY p_xyz[(x, y, z)] pxy print(H(X,Y):, round(entropy(p_xy.values()), 4)) # 2 print(H(Z):, round(entropy(marginalize(p_xyz, [2]).values()), 4)) # 0.544 print(H(X,Z):, round(entropy(marginalize(p_xyz, [0, 2]).values()), 4)) # 1.406 print(H(X,Y,Z):, round(entropy(p_xyz.values()), 4)) # 2因为Z由(X,Y)决定marginalize保留keep指定的维度索引比如[0,2]表示只保留X和Z把相同(X,Z)组合的概率累加。entropy的输出说明H(X,Y,Z)H(X,Y)2因为Z是X和Y的确定性函数不增加额外熵而H(X,Z)1.406比H(X)1高说明Z确实带了关于X的可预测信息因此互信息I(X;Z)H(X)H(Z)-H(X,Z)0.138bit。这种“先构造联合、再边缘化”的写法对任意维离散分布都成立比手推公式稳得多。4. 信道矩阵与信道容量BSC串接答案背后的两个边界从信源到信道是信息论的第二段。PDF第2章的核心是信道矩阵、后验概率、信道容量。很多人在2.1题里被“平均互信息”绕晕又在2.13题里被递推证明难住。这两个题分别对应信道分析的两种手段已知输入分布算互信息以及不看输入分布直接求容量边界。4.1 2.1题用全概率公式把每个中间量算出来2.1题给了一个2×2信道矩阵输入概率0.7/0.3信道矩阵为 [[5/6,1/6],[1/4,3/4]]。计算流程是固定的求联合概率P(a_i,b_j)P(a_i)*P(b_j|a_i)对联合概率按列求和得到P(b_j)用H(X,Y)-H(Y)得信道疑义度H(X|Y)平均互信息I(X;Y)H(X)-H(X|Y)。以这道题为例P(b1)0.7×5/60.3×1/479/120≈0.6583P(b2)41/120≈0.3417。H(X)0.881H(Y)0.926H(X,Y)≈1.580因此H(X|Y)H(X,Y)-H(Y)≈0.654I(X;Y)≈0.228 bit/符号。很多答案版本把0.698也写进去其实手算时保留4位小数即可重点是把四个中间概率都列出来。import math def entropy(probs): return -sum(p * math.log2(p) for p in probs if p 0) px [0.7, 0.3] P [[5/6, 1/6], [1/4, 3/4]] p_xy [[px[i] * P[i][j] for j in range(2)] for i in range(2)] p_y [sum(p_xy[i][j] for i in range(2)) for j in range(2)] H_X entropy(px) H_Y entropy(p_y) H_XY entropy([p_xy[i][j] for i in range(2) for j in range(2)]) I H_X H_Y - H_XY # 等价于 H(X)-H(X|Y) print(H(X|Y) %.4f, I(X;Y) %.4f % (H_XY - H_Y, I))这里联合熵直接用4个联合概率计算互信息用H(X)H(Y)-H(XY)可以少算一次条件熵。输出结果约为H(X|Y)0.6536I(X;Y)0.2277。把这道题做对之后2.4题的后验概率小题只是在同样的联合概率上做贝叶斯公式没有新东西。4.2 2.13串接BSC用递推式而不是矩阵乘法二进制对称信道BSC的参数是错误概率ε信道容量C1-H(ε)。2.13题问n个BSC串接后的错误概率。设第n级输出错误概率为p_n第n1级等于上一级正确时本次传错加上上一级错误时本次传对p_{n1} p_n*(1-ε) (1-p_n)*ε ε (1-2ε)*p_n初值p_1ε。这是一个一阶线性递推解得p_n 0.5*[1-(1-2ε)^n]。当n→∞如果0ε1则(1-2ε)^n→0p_n→0.5。此时输出和输入几乎独立信道容量趋近0。也就是说即使每级只有1%错误串接100级也几乎没有信息能过去这比简单地做矩阵连乘直观得多。def cascade_p(n, eps): p eps for _ in range(n - 1): p eps (1 - 2 * eps) * p return p for n in [1, 5, 20, 100]: print(n, round(cascade_p(n, 0.01), 6))输出能看到0.01、0.048、0.166、0.434逐渐逼近0.5。这说明工程上不会用多级独立BSC串联来降低误码率反而需要通过信道编码引入冗余让等效信道向“无噪”靠近。这个对照是2.13题真正想传递的信息。4.3 信道容量公式选型先看矩阵结构再套公式不同信道矩阵复杂度差别很大用错公式是常见失分点。我一般先按下面这个表做判断信道类型判断方法容量公式一一对应无噪信道每行每列只有一个非零1Clog₂r归并/扩张无噪信道某些列/行全为0或重复Clog₂r 或 log₂s取决于可区分输出数二元对称信道BSC2×2对角线1-ε反对角εC1-H(ε)强对称/均匀信道每行是同一组概率的排列每列也相同Clog₂r-H(行向量)准对称信道矩阵可按行列划分成若干对称子块按子块列概率和求解不能直接套公式这里的r是输入符号数s是输出符号数。BSC其实是强对称信道的特例。2.18到2.23题就是按这张表设计的先判断矩阵是不是置换矩阵、是不是行对称、能不能划分成对称块再选公式。如果矩阵不对称最稳妥的办法是用Arimoto-Blahut迭代算法做数值求容量而不是手推。5. 用Python复算这份PDF答案对数基底和条件概率分母是高频出错点把上面代码拼起来其实已经能覆盖PDF前两章大部分习题。但直接拿参考答案对数字时我遇到最多的是两类问题对数基底、条件概率的分母。这一章把检查方法写成一个最小校验工具。5.1 统一对数基底避免自然对数混入Python的math.log默认是自然对数如果用它算熵结果单位是nat不是bit。信息论课后答案全部用bit所以要除以log2import math def bit_entropy(probs): # 所有概率必须归一缺失概率会导致结果偏大或偏小 return -sum(p * math.log2(p) for p in probs if p 0) # 快速校验1.1点数和熵 s [i / 36 for i in [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]] print(round(bit_entropy(s), 4)) # 3.2744这里每次只校验一个小结果不要一次写完所有题。看到输出是3.2744而不是某些答案里的3.71就先检查自己的概率列表和题目里的“组合熵”定义是否一致再怀疑答案。对数基底的错误症状很典型结果与bit答案相差约1.4427倍也就是log₂e。5.2 后验概率分母一定要做全概率展开2.4题那种4×4信道矩阵问“收到b3条件下输入a2的概率”。很多人会把信道矩阵里的P(b3|a2)直接当作答案正确的是P(a2|b3) P(a2)*P(b3|a2) / Σ_i P(a_i)*P(b3|a_i)prior [0.1, 0.3, 0.2, 0.4] # 占位矩阵按你手上PDF第2.4题的实际数值替换 channel [ [0.2, 0.4, 0.3, 0.1], [0.3, 0.2, 0.1, 0.4], [0.1, 0.3, 0.4, 0.2], [0.4, 0.1, 0.2, 0.3], ] col 2 # b3 p_b3 sum(prior[i] * channel[i][col] for i in range(4)) p_a2_b3 prior[1] * channel[1][col] / p_b3 print(P(b3)%.4f, P(a2|b3)%.4f % (p_b3, p_a2_b3))P(b3)是四种输入下的加权平均这一步漏掉任何一个输入项后验概率马上错。用这段代码时channel矩阵要换成你手上版本的实际数值因为PDF第2.4题的矩阵在不同扫描版里有明显乱码。5.3 把PDF答案改造成回归测试集最后一个具体技巧把这份PDF当作回归测试集每做完一题就把关键结果写成assert。例如assert abs(bit_entropy([1/36]*6 [2/36]*15) - 4.3366) 1e-3 assert abs(bit_entropy([i/36 for i in [1,2,3,4,5,6,5,4,3,2,1]]) - 3.2744) 1e-3 assert abs(0.7 * 5/6 0.3 * 1/4 - 79/120) 1e-12这些断言同时承担两个作用一是以后改动熵函数或优化代码时不会把基础计算改坏二是当答案某个数字可疑时用断言直接暴露是答案错还是自己的概率空间构造错。我自己复算1.1时就发现组合熵先少乘了15后来靠这类断言定位到概率列表构造错误。信息论题的输入往往只有几行概率最值得花时间的不是背公式而是把概率空间定义写清楚、把分母算对、把单位统一成bit。本文还有配套的精品资源点击获取