
简介这份资源面向通信、数据存储方向的学习者与研究人员聚焦LDPC码的稀疏矩阵构造与解码实现。压缩包内共2个文件均为MATLAB脚本.m整体约2KB分别承担校验矩阵H与生成矩阵G的生成任务可根据用户预设的列重与行重参数构造符合要求的LDPC稀疏矩阵。列重指校验矩阵每列中1的个数行重指每行中1的个数二者直接影响码字的纠错能力与解码复杂度合理配置有助于在性能与效率之间取得平衡。生成矩阵用于将信息位映射为含冗余位的码字校验矩阵则服务于信念传播等消息传递解码算法其稀疏性可显著降低迭代计算量。已有401人学习关注。读者可借助脚本理解LDPC码从参数设定到矩阵生成、再到编码解码的完整链路并可按需调整行重列重适配不同信道环境与性能要求适合作为系统级仿真与算法验证的入门工具。1. 从两个 .m 文件说起LDPC 稀疏矩阵生成到底能解决什么问题很多人第一次接触 LDPC卡住的地方不是置信传播的迭代公式而是手里根本没有一个能跑起来的 H 矩阵。通信仿真里想验证译码性能得先有校验矩阵做数据存储的纠错链路也得先有稀疏矩阵。这个LDPC.zip里就两个文件ldpc_gen_h.m和ldpc_gen_g.m一个负责按指定列重、行重生成校验矩阵 H一个负责从 H 推出生成矩阵 G。它解决的就是「给定列重和行重快速拿到一对可用的 H/G」这件事适合做链路级仿真、算法验证、教学复现的工程师。列重Column Weight是 H 矩阵每列 1 的个数行重Row Weight是每行 1 的个数这两个参数直接决定码率和译码复杂度是 LDPC 稀疏矩阵设计里最先要定下来的东西。2. 列重行重怎么落到 H 矩阵生成逻辑与参数约束2.1 为什么 LDPC 要用稀疏矩阵而不是随便一个校验矩阵LDPC 的全称是低密度奇偶校验码低密度指的就是 H 矩阵里 1 的占比极低。假设码长 N1024列重 dv3那么整个 H 矩阵里 1 的总数就是 N×dv3072 个如果校验方程数 M512矩阵总元素是 512×1024≈52 万1 的占比不到 0.6%。这个稀疏性带来两个直接好处一是译码时置信传播算法只需要在非零元素上传递消息计算量从 O(N²) 降到 O(N×dv)二是稀疏结构让 Tanner 图上的环更少迭代译码不容易陷入局部最优。如果随手写一个稠密矩阵当 H译码复杂度会爆炸而且校验方程之间高度相关纠错能力反而下降。所以ldpc_gen_h.m的核心任务不是「生成一个矩阵」而是「生成一个满足列重行重约束、且尽量没有短环的稀疏矩阵」。常见做法是 Gallager 随机构造法或者 PEGProgressive Edge Growth算法前者简单但容易出四环后者复杂度高但围长更好。这个脚本大概率走的是 Gallager 那一套因为代码量小、参数直观。2.2 列重、行重、码率三者的换算关系在动手调脚本之前得先把三个参数的关系理清楚不然生成出来的矩阵要么维度对不上要么码率不是你要的。参数符号含义约束码长N码字比特数正整数通常 2 的幂或准循环倍数校验方程数MH 矩阵行数M N - KK 为信息位长度列重dv每列 1 的个数通常取 2~6太大稀疏性变差行重dc每行 1 的个数dc N×dv / M必须为整数码率RK/NR 1 - dv/dc关键约束是N×dv M×dc也就是 1 的总数按行算和按列算必须相等。如果你指定 N1024、dv3、dc6那么 M 1024×3/6 512码率 R 1 - 3/6 0.5。如果 dc 除不尽脚本要么报错要么自动调整这就是后面避坑章节要讲的问题。2.3 跑通 ldpc_gen_h.m从参数到 H 矩阵MATLAB 里调用这个脚本一般流程是先设参数再调函数最后检查矩阵稀疏度。下面是我常用的调用骨架% 参数设置 N 1024; % 码长 dv 3; % 列重 dc 6; % 行重 M N * dv / dc; % 校验方程数必须为整数 % 检查参数合法性 if mod(N * dv, dc) ~ 0 error(行重列重与码长不匹配N*dv 必须能被 dc 整除); end % 调用生成脚本函数名以实际文件为准 H ldpc_gen_h(N, dv, dc); % 验证稀疏性与维度 [rows, cols] size(H); fprintf(H 矩阵维度: %d x %d\n, rows, cols); fprintf(非零元素占比: %.4f%%\n, nnz(H) / numel(H) * 100); % 检查每列每行的重是否满足设定 col_weight sum(H, 1); row_weight sum(H, 2); fprintf(列重范围: %d ~ %d\n, min(col_weight), max(col_weight)); fprintf(行重范围: %d ~ %d\n, min(row_weight), max(row_weight));这段代码的逻辑说明先算 M确保N×dv能被dc整除这是 Gallager 构造法的硬性前提。然后调ldpc_gen_h拿到 H接着用nnz统计非零元素占比正常应该在dv/N量级也就是 0.3% 左右。最后检查每列每行的重如果列重不全是 dv、行重不全是 dc说明脚本内部做了随机化或者填充需要看代码确认。参数怎么改N 一般取 648、1296、1944 这些准循环 LDPC 常用长度dv 取 3 性能比较均衡dc 根据码率反推。如果要做 0.75 码率dv3 时 dc12MN/4。注意 dc 越大行重越高Tanner 图上的校验节点度数越大译码收敛越快但单次迭代计算量也越大。2.4 从 H 到 Gldpc_gen_g.m 的生成矩阵推导拿到 H 之后编码还需要生成矩阵 G。ldpc_gen_g.m的任务就是从 H 推出 G使得G×H^T 0在 GF(2) 域上。常见做法是对 H 做高斯消元化成[I | P]形式然后 G [P^T | I]。这个过程在 MATLAB 里可以用gf或者mod手动实现。% 假设 H 已经生成维度 M x N % 对 H 做 GF(2) 高斯消元化为系统形式 [I_M | P] H_sys rref(mod(H, 2)); % 注意rref 在 GF(2) 下需自行处理 % 提取 P 矩阵 P H_sys(:, M1:N); % 构造生成矩阵 G [P^T | I_K] G [P eye(N-M)]; % 验证正交性G * H^T 应为全零矩阵 check mod(G * H, 2); if any(check(:)) warning(G*H^T 不为零检查消元过程); else disp(G 与 H 正交性验证通过); end逻辑说明rref是 MATLAB 内置的行最简形函数但它不保证在 GF(2) 下正确因为浮点运算会引入误差。更稳妥的做法是自己写 GF(2) 消元用xor代替加减。参数方面M 和 N 从 H 的维度直接取KN-M 是信息位长度。验证步骤不能省G×H^T如果不是全零编码出来的码字根本不在零空间里译码必然失败。提示如果ldpc_gen_g.m内部已经处理了 GF(2) 消元直接调就行如果它假设 H 已经是系统形式那得先用ldpc_gen_h.m生成再手动消元。两个脚本的接口约定要看文件头的注释。3. 避坑与排查列重行重生成 LDPC 矩阵时最容易翻车的五件事3.1 现象脚本报「维度不匹配」或生成的 H 矩阵行列数不对原因N×dv不能被dc整除或者脚本内部把 M 算成了浮点数。Gallager 构造法要求M N×dv/dc必须是整数否则最后一行的行重会和其他行不一致脚本可能直接报错或者静默截断。解决在调用前加一行assert(mod(N*dv, dc) 0, 参数不满足整除约束)。如果确实需要非整数比得改用 PEG 或者准循环构造不能硬套 Gallager。3.2 现象生成的 H 矩阵列重不均匀有的列重是 2 有的列重是 4原因随机放置 1 的时候没有做列重控制或者用了randperm但没检查每列已放置的数量。Gallager 构造法是把 H 分成 dv 个子矩阵每个子矩阵每列只有一个 1如果子矩阵内部随机化时冲突了就会出现列重偏差。解决生成后立刻统计sum(H,1)和sum(H,2)如果偏差超过 1说明脚本的随机化逻辑有缺陷。可以在脚本里加一个 while 循环对不满足列重的列重新随机直到所有列重严格等于 dv。3.3 现象译码仿真时 BER 曲线异常高几乎不收敛原因H 矩阵里出现了大量四环4-cycle也就是 Tanner 图上两个校验节点和两个变量节点之间形成了长度为 4 的环。四环会让置信传播的消息在环上反复震荡迭代译码无法收敛。解决生成 H 后检查四环数量。简单方法是计算H×H^T如果非对角线上有大于 1 的元素说明存在共享两个变量节点的校验节点对即四环。可以用 PEG 算法重新生成或者在 Gallager 基础上做列交换来打散四环。3.4 现象ldpc_gen_g.m跑完得到的 G 矩阵维度不对或者G×H^T不为零原因高斯消元过程中没有在 GF(2) 域下运算MATLAB 的rref默认是浮点运算遇到11会得到 2 而不是 0导致消元结果错误。解决自己写 GF(2) 消元所有加减用xor乘用。或者用通信工具箱的gf对象但要注意gf的运算速度较慢大矩阵下可能卡住。消元后必须验证mod(G*H,2)是否全零。3.5 现象码长 N 取 1024 时脚本能跑取 648 时报错或结果异常原因某些 LDPC 构造法对码长有隐含要求比如必须是准循环的倍数或者 N 必须能被 dv 整除。648 能被 3 整除但 648/6108如果脚本内部假设 N 是 2 的幂就会出问题。解决先看脚本文件头有没有对 N 的约束说明。如果没有用几个不同 N 值试跑记录哪些能过哪些不能反推约束条件。常见做法是 N 取 2 的幂或者 3×2^k 形式兼容性最好。4. 进阶用法用生成矩阵做编码、用校验矩阵做译码验证4.1 从 G 矩阵完成一次完整编码拿到 G 之后编码就是信息位向量乘以 G。假设信息位msg是 1×K 的二进制向量码字cw mod(msg * G, 2)得到 1×N 的码字。下面是一个完整的编码加验证流程% 生成随机信息位 K N - M; msg randi([0 1], 1, K); % 编码 cw mod(msg * G, 2); % 验证码字满足校验方程 syndrome mod(cw * H, 2); if any(syndrome) error(编码失败码字不满足 H*cw^T 0); else fprintf(编码成功码字长度 %d校验通过\n, length(cw)); end逻辑说明msg * G在 GF(2) 下就是模 2 乘法mod(...,2)保证结果只有 0 和 1。syndrome是伴随式全零说明码字合法。这一步是后续译码仿真的前提如果伴随式不为零译码器再强也救不回来。4.2 用 H 矩阵跑一次置信传播译码译码部分这个压缩包没有直接提供脚本但有了 H 矩阵置信传播BP译码可以自己写。核心是初始化对数似然比LLR然后迭代更新变量节点和校验节点的消息。下面是一个最小化的 BP 译码框架% 假设接收到的 LLR 为 llr长度 N % H 为 M x N 稀疏矩阵 max_iter 50; [M, N] size(H); L llr; % 初始 LLR for iter 1:max_iter % 校验节点更新 for j 1:M idx find(H(j, :)); for k idx others idx(idx ~ k); sign_prod prod(sign(L(others))); min_val min(abs(L(others))); R(j, k) sign_prod * min_val; end end % 变量节点更新 for i 1:N idx find(H(:, i)); L(i) llr(i) sum(R(idx, i)); end % 硬判决与伴随式检查 cw_hat L 0; if ~any(mod(cw_hat * H, 2)) fprintf(第 %d 次迭代译码成功\n, iter); break; end end逻辑说明这是最小和Min-Sum译码的简化版用min代替了tanh乘积复杂度低但性能略差。R(j,k)是校验节点 j 传给变量节点 k 的消息L(i)是变量节点的后验 LLR。每次迭代后做硬判决如果伴随式全零就提前退出。参数max_iter一般取 50码长越长需要迭代次数越多但超过 100 次基本没有增益。4.3 验证生成矩阵和校验矩阵是否匹配的一个小技巧有时候ldpc_gen_h.m和ldpc_gen_g.m是分开调的两个脚本可能用了不同的随机种子导致生成的 H 和 G 不匹配。验证方法很简单随机生成一组信息位用 G 编码得到码字再用 H 算伴随式。如果伴随式全零说明匹配否则两个矩阵来自不同的构造过程。我一般会在脚本开头固定随机种子rng(42)这样每次生成的 H 和 G 都是确定性的方便复现和调试。如果要做蒙特卡洛仿真再在外层循环里改种子。注意MATLAB 的rng在不同版本间行为可能略有差异跨版本复现时最好把生成的 H 和 G 保存成.mat文件直接加载而不是每次重新生成。4.4 从列重行重到性能调优的一个习惯列重和行重不是随便设的。dv3 是大多数标准 LDPC 码的默认选择因为它在稀疏性和纠错能力之间平衡得最好。dv2 的码虽然更稀疏但容易出现停止集译码性能会掉。dv 超过 6 之后H 矩阵的稀疏性明显变差BP 译码的单次迭代计算量上升而纠错增益递减。行重 dc 由码率决定R0.5 时 dc2×dvR0.75 时 dc4×dv。如果要做高码率dc 会比较大这时候要注意 Tanner 图上校验节点的度数度数太高会让最小和译码的近似误差累积。从那以后我每次生成 H 矩阵都会先跑一遍sum(H,1)和sum(H,2)确认列重行重严格符合设定再算一次H*H看四环数量最后用随机信息位做一次编码-译码闭环。这三步走完才敢把矩阵丢进仿真链路里。希望帮到你。本文还有配套的精品资源点击获取