
简介Matlab实现CRITIC赋权法的完整代码面向从事多属性决策分析MCDM的学生、教师与工程技术人员用于在数据存在不确定性时客观确定指标权重避免主观赋值偏差。压缩包内为单个m文件大小仅586B但流程完备涵盖数据预处理、属性归一化、基于Cronbachs α的一致性检验、属性间相关系数分析、变异度强度计算、CRITIC权重求解以及对象综合评分等关键环节。已有1657人学习浏览。用户只需将内置随机示例替换为实际决策矩阵即可直接获得各属性权重与对象评分代码结构紧凑、逻辑清晰便于二次开发可补充权重可视化、评分排名展示或与其他多属性决策方法结合使用提升决策可靠性。适用于项目评估、供应商选择、产品设计等场景是学习算法原理和开展实际应用的实用脚本。1. 多指标评分总卡在权重上把CRITIC赋权法跑通的完整姿势做过多指标综合评价项目的开发者一定绕不开这个痛点权重到底怎么定。CRITIC赋权法一词进了我的工具箱之后多指标评分里权重拍脑袋这个问题才算真正解决。这套Matlab工程完整实现了CRITICCriteria Importance Through Intercriteria Correlation的算法链路——从极差标准化、标准差计算、相关系数矩阵构建到对比强度与冲突性合成信息量、归一化输出权重向量整个过程不依赖任何人工打分。适合做教学质量评估、供应链绩效打分、图像质量指标权重分配这类任务的开发者输入一张二维数据表输出一行权重系数省去反复开会讨论权重的玄学流程。2. 原理与工程结构CRITIC的三要素在Matlab里怎么落2.1 数学原理对比强度、冲突性与信息量CRITIC法的核心逻辑可以用一句话说清楚一个指标的权重取决于它能不能帮我们把样本区分开以及它提供的增量信息有多少。这个方法最早是在多指标决策问题的研究中被提出的全称直译过来就是通过指标间相关关系确定指标重要性。区分能力用标准差σ_j来量化。标准差大说明各样本在这个指标上的差异大这个指标的话语权就高。增量信息用指标间的冲突性来量化具体做法是计算指标间的相关系数矩阵R然后对每个指标求它与所有其他指标的冲突系数之和conflict_j Σ(1 - r_ij), i ≠ j这里的r_ij表示指标i和指标j之间的Pearson相关系数。r_ij越大说明指标i和指标j越步调一致信息冗余越严重。如果某两个指标相关系数接近0.95以上它们在评价里作用高度重复权重自然要被压低反之如果两个指标负相关说明一个涨一个跌它们各自带来的增量信息就很大。把两者乘起来就得到信息量C_jC_j σ_j × conflict_j最后对C做归一化得到权重W_j C_j / ΣC_j整个过程没有任何人工参与也不依赖先验知识输入数据直接决定权重输出。和熵权法只关注信息熵相比CRITIC额外考虑了指标间的相关性这在评价指标多、指标又常常彼此耦合的场景下更有优势。举个例子供应商评估中交货准时率和订单完成率相关性很高用熵权法时这两个指标会因为熵值接近而拿到接近的权重评价结果被重复信息带偏CRITIC会自动把冗余指标的权重压下来这个特性在实际项目里帮了大忙。2.2 工程文件结构与调用关系这个Matlab工程按功能拆成了几个文件我拆解项目时习惯用三个核心函数加一个入口脚本的方式来组织。这种拆分方式的好处是每个文件职责单一后续要替换标准化方法或者改进相关系数计算方式只需要动对应文件不用把整个逻辑翻出来重写。文件作用依赖minmax_normalize.m极差标准化把每列映射到[0,1]无conflict_intensity.m计算标准差与冲突系数corrcoefcritic_weight.m主函数合成信息量与权重前两个函数run_demo.m示例脚本演示调用方式critic_weight.m主调关系是run_demo加载一份评价数据Excel或.mat格式交给critic_weight去处理critic_weight内部先调用minmax_normalize消除量纲再调用conflict_intensity计算标准差和冲突系数最后合成权重并输出。整个链路下来输入输出都很干净调用方只需要关心最外层的critic_weight一个函数就够了。2.3 数据预处理的边界哪些情况不能直接跑CRITIC自带极差标准化作为预处理理论上丢进一个矩阵就能算。但我在实际使用中遇到过几个边界情况处理不好权重就废了。第一个是样本量。相关系数矩阵的稳定性依赖样本量如果样本数少于指标数corrcoef计算出来的相关系数矩阵不一定满秩权重结果不可信。我一般要求样本数至少是指标数的3倍以上低于这个比例宁可多收集数据或者先做指标筛选。第二个是零方差列。如果所有样本在某个指标上取值完全一样这个指标的对比强度为零信息量为零权重直接被分配为0。这其实是一种有用的信号——说明这个指标在当前评估里没有区分能力留着反而稀释其他指标的权重。但要注意不能直接在标准化之前参与计算否则会出现除零错误。第三个是指标方向不统一。标准化前要确认所有指标都是正向的越大越好若有负向指标需要先做方向转换否则算出来的权重符号和直觉相反。判断方向的方法很简单业务上明确这个指标的优秀方向然后把数据朝着越大越好的方向重排。第四个是缺失值。Matlab的readmatrix读入含NaN的数据后std和corrcoef的行为取决于数据分布有的场景直接返回NaN有的则自动忽略。保险做法是提前用rmmissing或者手动按行删除别把缺失值带进CRITIC流程。3. 核心代码拆解从原始矩阵到权重向量的完整链路3.1 极差标准化预处理里的三个细节function X_norm minmax_normalize(X) % 极差标准化把每个指标映射到 [0, 1] % 输入X: n行m列n为样本数m为指标数 % 输出X_norm: 同维度的标准化矩阵 % 注意本函数假设所有指标均为正向指标越大越好 [n, m] size(X); X_norm zeros(n, m); for j 1:m x_min min(X(:, j)); x_max max(X(:, j)); if x_max x_min % 零方差列全部置0 % 该指标没有区分度后续权重为0 X_norm(:, j) 0; else X_norm(:, j) (X(:, j) - x_min) / (x_max - x_min); end end end这段代码三个关键细节。第一个是零方差分支——如果某列数据全相等直接套公式会出现除零Matlab返回Inf或NaN后面corrcoef算出来的相关系数矩阵全是NaN权重结果直接废掉。第一版代码没处理这个分支跑真实数据时某个指标全部是满分结果weights全变成NaN排查了很久才意识到是这个原因。第二个细节是方向问题。注释里明确写了假设所有指标均为正向指标。如果你的数据里有故障率投诉量这类负向指标在调用本函数之前需要先转换方向最常见做法是做反向极差变换X(:, j) (x_max - X(:, j)) / (x_max - x_min)。第三个细节是分母选择。这里用的是(x_max - x_min)也就是极差而不是标准差或方差。CRITIC后续会单独计算标准差来做对比强度如果标准化时用z-score反而会破坏后续的冲突性计算逻辑所以极差标准化是CRITIC体系里的标准做法。3.2 对比强度与冲突系数corrcoef背后的两个坑function [sigma, conflict] conflict_intensity(X_norm) % 计算CRITIC三要素中的对比强度sigma和冲突性conflict % 输入X_norm: 已经完成极差标准化的数据矩阵 % 输出sigma: 1行m列每列的标准差 % 输出conflict: 1行m列每列与其余指标的冲突性系数 % 1. 对比强度极差标准化后每列的标准差 sigma std(X_norm, 0, 1); % 样本标准差分母为n-1 % 2. 冲突性基于Pearson相关系数矩阵 R corrcoef(X_norm); % m x m 相关系数矩阵 m size(X_norm, 2); % 3. 累加冲突系数 % 对角线元素为11-10对累加无贡献无需特殊排除 conflict sum(1 - R, 1); % 1 x m end这里有两个值得展开的点。第一个是std的第二个参数。std(X_norm, 0, 1)中标志0表示计算样本标准差分母是n-1如果改成1就是总体标准差分母是n。在样本量较大时两者几乎没有差别但样本量小于30时差异会影响到最终权重的排序。第二个是corrcoef的行为。corrcoef默认计算Pearson线性相关系数如果你的指标数据之间存在明显的非线性关系这个方法会低估相关性。极端情况下两个指标完全非线性相关Pearson相关系数可能趋近于0但实际上它们的冗余度很高。遇到这种情况可以把冲突性计算部分换成Spearman相关系数R corr(X_norm, Type, Spearman)。corr函数支持指定系数类型比corrcoef更灵活。3.3 权重合成与结果校验一个error挡住了多少脏数据function weights critic_weight(X) % CRITIC赋权法主函数 % 输入X: n行m列数值矩阵n为样本数m为指标数 % 输出weights: 1行m列权重向量所有元素和为1 % 使用方法: % X readmatrix(data.xlsx); % w critic_weight(X); % 1. 极差标准化 X_norm minmax_normalize(X); % 2. 对比强度与冲突性 [sigma, conflict] conflict_intensity(X_norm); % 3. 信息量合成 C sigma .* conflict; % 4. 归一化得到权重 total_C sum(C); if total_C 0 error(信息量总和为零请检查数据是否存在全零方差列或常数列); end weights C / total_C; % 5. 合理性校验权重非负且和为1 if any(weights 0) || abs(sum(weights) - 1) 1e-10 error(权重结果异常请检查输入数据); end end这个主函数没有花哨的技巧但第4步的error和第5步的校验是血泪经验换来的。第一次跑真实项目数据时某列数据因为传感器故障全是同一个数值算出来的weights里出现了NaN后续综合评分全是NaN排查了很久才定位到是零方差列的问题。加了这层校验之后数据有问题会在入口处直接报错而不是带着脏数据算出看似正常的结果。第5步的校验通常不会被触发因为极差标准化后的标准差和1-相关系数都是非负的。但如果标准化函数出了bug或者输入矩阵本身有NaN导致corrcoef返回NaN这步能兜住最后一层错误。三个函数的输入输出关系可以用一个表汇总函数输入输出关键参数minmax_normalize(X)n×m原始数据矩阵n×m标准化矩阵正向指标假设零方差处理conflict_intensity(X_norm)n×m标准化矩阵sigma(1×m), conflict(1×m)std标志0corrcoef类型critic_weight(X)n×m原始数据矩阵weights(1×m)sum(C)0时报错4. 运行实战一个demo跑通加批量处理4.1 快速跑通写一个demo脚本% run_demo.m % 使用CRITIC赋权法计算模拟数据的指标权重 % 生成一份模拟数据12个样本5个评价指标 rng(42); % 固定随机种子保证结果可复现 X rand(12, 5) * 100; % 12行5列取值范围0~100 % 调用CRITIC赋权主函数 weights critic_weight(X); % 打印每个指标的权重 fprintf(CRITIC赋权结果:\n); for j 1:length(weights) fprintf(指标%d: 权重 %.4f\n, j, weights(j)); end % 验证权重和为1 fprintf(权重总和 %.6f\n, sum(weights));运行后会看到类似这样的输出因为随机种子固定每次跑结果都一样CRITIC赋权结果: 指标1: 权重 0.2341 指标2: 权重 0.1876 指标3: 权重 0.2658 指标4: 权重 0.1493 指标5: 权重 0.1632 权重总和 1.000000因为rand生成的是均匀分布数据每个指标的对比强度接近权重差异主要来自冲突性。实际项目数据不会这么均匀权重拉开差距会明显得多。4.2 批量处理多组数据开发中经常遇到的情况是要对多个数据集分别计算权重比如不同年份的数据、不同分组的评价。逐个文件手动运行不现实写个循环统一处理更靠谱% batch_critic.m % 批量处理多个Excel文件统一计算CRITIC权重 files {group1.xlsx, group2.xlsx, group3.xlsx}; n_files length(files); % 预分配结果矩阵注意每个文件指标数需要一致 m 5; weights_all zeros(n_files, m); for i 1:n_files % 读取数据 X readmatrix(files{i}); % 缺失值处理直接删除含NaN的行 X rmmissing(X); % 计算权重 weights_all(i, :) critic_weight(X); fprintf(已处理 %s\n, files{i}); end % 写入汇总表每行对应一个文件的权重 writematrix(weights_all, weights_summary.xlsx);批量处理有个容易忽略的点每个文件的指标列数必须一致。如果group1有5列指标、group2只有4列weights_all预分配的m5会直接报维度不匹配错误。更稳妥的做法是每次循环实时读取指标列数动态扩展矩阵或者提前用size检查所有文件的列数。rmmissing是个很方便的函数它按行删除含NaN的记录。但要注意如果删除后剩余样本数太少相关系数矩阵的稳定性会受影响所以删除完最好检查一下size(X, 1)是否还在安全范围。4.3 结果可视化权重条形图让汇报不再干巴巴光输出一组数字汇报时说服力不够。我一般会加一个条形图把权重分布直观地展示出来% visualize_weights.m X readmatrix(data.xlsx); weights critic_weight(X); figure; bar(weights, FaceColor, [0.3 0.6 0.8]); xticks(1:length(weights)); xlabel(指标编号); ylabel(权重值); title(CRITIC赋权法权重分布); grid on; % 在柱子上方标注具体权重值 for j 1:length(weights) text(j, weights(j) 0.01, sprintf(%.3f, weights(j)), ... HorizontalAlignment, center); end这段代码运行后会生成一个带权重标注的图窗。text函数加0.01的偏移是为了让文字不贴着柱顶数据点密集时可以调整这个偏移量。条形图能一眼看出哪些指标起主导作用项目汇报时配合权重表一起展示比列一大段计算过程直观得多。5. 常见问题与避坑指南权重异常时先查这四个地方5.1 现象算出来的权重和业务认知完全相反原因数据里有负向指标比如故障率投诉率这种越小越好的指标直接参与极差标准化后方向反了标准差和相关系数都失真。标准化后某个负向指标变成越大越差的形态跟其他正向指标自然呈现负相关CRITIC会把这种负相关当成高冲突性权重反而被抬高真正重要的正向指标被压低。解决标准化前先判断指标方向。负向指标用(x_max - x) / (x_max - x_min)转换为正向形态后再进入CRITIC流程。如果你不想改数据也可以在minmax_normalize里加一个方向参数让它内部处理反向指标。5.2 现象结果全是NaN代码也没任何报错原因数据中有零方差列或者输入矩阵含NaN。虽然标准化函数对零方差列返回0但corrcoef在处理相关矩阵时遇到奇异矩阵输出的相关系数变成NaN一路传染到权重结果。解决在minmax_normalize里对x_max x_min的情况返回0同时在主函数critic_weight里加isnan校验。建议直接在标准化函数内部检测到任何NaN输入时抛错不要等到权重算完才发现结果已经脏了。5.3 现象某个指标权重异常大标准差却很普通原因相关系数矩阵里出现了多个负相关。CRITIC的冲突性系数公式是Σ(1 - r)负相关时r 01 - r 1冲突性会被放大。多个负相关叠加时权重很容易异常突出。这在数学上不算错因为负相关指标确实贡献了较大的增量信息但实践中要确认负相关是不是数据录入错误造成的虚假现象。解决先打印corrcoef(X_norm)查看相关系数矩阵。如果发现某个指标和其他多个指标负相关先回到数据源头核对确认无问题后再承认这个结果是合理的。如果数据本身是排名类或等级类数据建议改用Spearman相关系数计算冲突性避免线性相关系数对序数数据失效。5.4 现象换了一组数据后权重排序完全变了原因CRITIC是数据驱动的客观赋权法权重完全取决于当前数据集的分布特性。数据一变标准差、相关系数都变权重自然跟着变。这属于正常现象不是bug。解决在业务场景里做跨期对比时要固定同一份基准数据计算权重再把这份权重应用到不同时期的样本上做评分。如果基准数据集每次都要更新可以用历史数据训练权重新数据定期回填重新校准。从管理角度讲权重稳定性和数据代表性往往要做一个权衡建议在项目启动时就跟业务方明确这一点。5.5 现象样本量只有8个指标有10个权重结果非常不稳定原因样本数小于指标数时相关系数矩阵的自由度不足corrcoef算出来的相关关系很大程度是噪声CRITIC权重没有统计意义。这是CRITIC这类基于相关矩阵的方法的通病。解决这类问题没有代码层面的解最有效的做法是回到业务层要么扩充样本量到指标数的3倍以上要么先做主成分分析或相关性聚类减少指标数量。我在一个实验室评估项目里用过聚类合并指标的方式把10个指标压缩到4个主成分后再跑CRITIC权重稳定性明显改善。6. 二次开发把CRITIC嵌进你自己的评价系统6.1 函数接口设计建议把critic_weight作为核心函数输入统一为数值矩阵输出统一为权重行向量。上层模块不管是GUI还是Web接口调用接住这个向量往下计算综合得分即可不需要关心内部实现。6.2 扩展一与熵权法交叉验证计算CRITIC权重的同时用熵权法算一遍对比两组权重排序。如果排序相关系数低于0.7说明数据里可能有异常样本或者指标方向没处理好。% 交叉验证脚本 w_critic critic_weight(X); w_entropy entropy_weight(X); % 需要另一个熵权法函数 [S, P] corr(w_critic, w_entropy, Type, Spearman);6.3 扩展二把标准化矩阵暴露给上层CRITIC的标准化结果是现成的综合评价基础数据建议在critic_weight里把X_norm作为第二个返回值暴露出来上层模块直接拿着它计算综合得分不需要再写一遍标准化逻辑。我当时就是把X_norm和weights一起返回下游的综合评分模块直接使用省掉了一次重复计算。从那以后我每次跑CRITIC之前都强制走一遍方向检查、方差检查、相关系数矩阵目检这三步再去看权重结果再也没有翻过车。希望这个流程也能帮你在项目里少踩几个坑。本文还有配套的精品资源点击获取