☰
MATLAB实现CRITIC赋权法:原理、代码与避坑指南
2026/10/21 17:11:16 网站建设 项目流程

简介:CRITIC赋权法是一种综合考虑一致性、相关性与强度指标的多属性决策权重客观确定方法,由Dimitriades和Tzafestas于1995年提出,适用于项目选择、供应商评估、产品设计等具有不确定性和模糊性的决策场景。对应Matlab实现压缩包内仅有1个m文件,体积仅586B,代码结构精简,包含数据预处理、属性归一化、一致性指标计算、属性间相关性分析、变异强度度量、权重求解及对象综合打分等完整子函数。用户只需将内置的随机示例数据替换为实际问题的决策矩阵,即可直接获得各属性的CRITIC权重与每个方案的量化评分。该资源目前已有1657人学习下载,适合正在学习多属性决策分析方法或需要快速获取可运行Matlab赋权脚本的本科生、研究生及科研工程人员使用,便于在此基础上进一步扩展可视化展示或与其他评价方法结合。

1. 什么时候你需要一份MATLAB版的CRITIC赋权法

当我需要给一组评价指标分配权重时,CRITIC赋权法是我在MATLAB里优先尝试的客观赋权方法。它不需要专家打分,也不依赖决策者的经验,只靠数据本身的信息量来定权重。相比熵权法只盯着指标的离散程度,CRITIC还额外考虑了指标之间的冲突性,特别适合处理那些指标彼此相关、甚至高度重合的评价体系。有人喜欢抱着“从入门到精通”式的教程啃语法,但我更倾向于把一个能直接跑的项目当成最好的语法书。

这份资源是一个完整的MATLAB实现包,解决的是从Excel数据到权重输出的全部流程:读取数据、正向化处理、极差标准化、计算标准差与相关系数、合成信息量、归一化得到权重,最后还能画出一张权重柱状图。适用人群很明确:写论文需要客观权重的研究生、做综合评价报告的开发者,以及刚入门MATLAB但想把算法落地的初学者。

2. 原理与代码结构:先看懂信息量公式,再看懂文件分工

2.1 CRITIC的算法骨架:对比强度与冲突性怎么合成信息量

CRITIC的英文全称是Criteria Importance Through Intercriteria Correlation,核心思路可以拆成两个维度。第一个维度叫对比强度,衡量的是同一个指标在不同样本之间的差异大小。如果某个指标在所有样本上的取值几乎一样,它对“区分样本好坏”就没什么贡献,权重应该给低;反过来,取值差异越大,说明它携带的区分信息越多,权重应该给高。实现时通常用标准差表示,差异大则标准差大。

第二个维度是冲突性,衡量的是某个指标与其余指标之间的相关性。假如两个指标高度正相关,说明它们反映的信息大量重叠,这时候两个指标都拿高权重就相当于把同一份信息重复计入了;如果指标之间相关性弱,甚至负相关,说明各自携带独立信息,应当保留较高的权重。CRITIC用相关系数矩阵来计算冲突性,具体做法是把某个指标与其余所有指标的相关系数取绝对值后,用1减去这个绝对值再求和。

两个维度合在一起,就得到每个指标的信息量C:对比强度(标准差)乘以冲突性(熵)。最后把所有指标的信息量做归一化,占比高的指标权重自然就大。这个思路和AHP最大区别在于没有任何主观打分环节,也和熵权法有明显区别——熵权法只看离散程度,CRITIC多看了指标间的相关性,所以当你的指标体系里存在强相关指标时,CRITIC的结果往往更合理。

2.2 源码包结构:主脚本、核心函数与数据文件的分工

一个合格的MATLAB实现不会把所有代码塞进一个文件里,通常分成三部分:主控脚本负责流程编排,核心函数负责算法计算,数据文件负责输入输出。我拆过的这套资源大概也是这个结构,对应关系如下表所示:

文件作用需要改动的内容
main_critic.m主脚本:读取数据、调用函数、打印权重、绘图数据路径、指标方向配置
critic_core.m核心计算:标准化、标准差、相关系数、信息量、权重归一化一般不用动
normalize_minmax.m极差标准化实现若想换z-score可替换
data_input.xlsx样例数据用你自己的数据覆盖

如果你拿到的包里函数名不完全一样,先打开main_critic.m,按调用顺序往下追,一般能找到“标准化”和“计算权重”两个关键函数。这种结构的好处是职责边界清晰:换数据不用碰算法,改算法不用动主脚本。做二次开发的时候,只需要盯住critic_core.m这一个文件就够了。

2.3 为什么选MATLAB:矩阵化写法省掉三层for循环

学过C语言指针的人第一次接触MATLAB会有点不适应:矩阵不用手动遍历,整列运算一句话就完成。这个项目恰恰是典型的矩阵化场景——标准差用std按列算,相关系数用corrcoef一次得矩阵,信息量用点乘完成,全部不需要显式循环。代码短,出错概率也低。

对比Python实现,MATLAB的优势在于统计函数开箱即用,而且画图非常方便。corrcoef、std这些函数在MATLAB里是基础功能,不需要额外装包;bar画权重图也是一行代码的事。做算法验证的时候,这种“数据进来,图和表一起出去”的工作流很顺手。

3. 核心代码逐段拆解:数据从Excel到权重输出的完整链路

3.1 第1步:数据读入与指标正向化

先处理数据入口。假设你的Excel表第1列是样本编号,第2列开始是指标值,第一行是指标名。用readmatrix读进来后,去掉样本编号列,得到纯数值矩阵:

% main_critic.m 片段:读数据 + 正向化 % 数据表结构:第1列样本ID,其余列为指标值,第1行是指标名 data = readmatrix('data_input.xlsx', 'NumHeaderLines', 1); X = data(:, 2:end); % 去掉样本ID列,得到[m, n]数据矩阵 [n, m] = size(X); % n个样本,m个指标 % 成本型指标列号(值越小越好),按实际指标体系修改 costCols = [2, 4]; % 正向化:用倒数把“越小越好”转成“越大越好” X(:, costCols) = 1 ./ X(:, costCols);

正向化这一步非常关键。CRITIC本身不识别指标方向,成本型指标如果不处理,标准差算出来方向是反的,权重会和实际含义打架。这里用倒数而不是取负数,是因为后续要做极差标准化,倒数变换仍然保持正向单调性,而负数会让标准化后的区间变成[0,1]的反向映射,容易把下游计算搞乱。

参数说明:costCols按你的指标体系配置,如果全部是收益型指标(越大越好),这行直接留空或删除;如果数据量纲差异极大,比如有的指标是百分比、有的是金额,不需要提前归一化,极差标准化会在下一步统一处理。

3.2 第2步:极差法归一化把量纲抹平

用min-max极差法把每一列压缩到[0,1]区间,这样不同量纲的指标就能放在同一个尺度下比较。代码很短,但背后有讲究:

% 极差法归一化:单独对每列做,不跨列 minVal = min(X, [], 1); % 每个指标的最小值 maxVal = max(X, [], 1); % 每个指标的最大值 X_norm = (X - minVal) ./ (maxVal - minVal);

这里选择极差法而不是z-score标准化,是因为CRITIC的冲突性计算依赖相关系数矩阵,极差法把所有指标统一到[0,1]区间后,相关系数的数值解释更直观;z-score会把数据变成均值为0、方差为1的分布,本身也没问题,但在信息量合成阶段,标准差会恒定为1,对比强度完全丢失。换句话说,选了z-score就等于把CRITIC的“对比强度”维度废掉了,权重只由冲突性决定,这是最容易被忽略的坑。

参数说明:min(X, [], 1)里的第三个参数1表示沿列方向取最小值,写成min(X)默认是对整个矩阵取全局最小值,那就错了。每一列必须独立标准化,否则量纲差异会顺着列间比较混进来。

3.3 第3步:标准差与相关系数计算

这一步是CRITIC的核心,对比强度和冲突性在这里算出来:

% 对比强度:按列计算标准差,0表示样本标准差(除以n-1) sigma = std(X_norm, 0, 1); % 冲突性:相关系数矩阵,取绝对值后再做1-r变换 R = corrcoef(X_norm); % 减去1是为了去掉矩阵对角线上“指标与自身的相关系数” conflict = sum(1 - abs(R), 1) - 1;

std(X_norm, 0, 1)的第二个参数0表示除以n-1,如果写1会除以n。两种结果绝对值不同,但后面要做归一化,只要所有指标统一用同一种设置,权重结果完全一致,所以这里不用纠结。

相关系数矩阵R的维度是m×m,对角线永远是1。abs(R)的处理值得单独说:如果两个指标强负相关,说明它们反映的是同一件事的相反方向,本质上也是信息重复,取绝对值后冲突性降低,权重会被合理压低。如果不取绝对值,负相关会被当作“高冲突”处理,权重反而变高,这就和CRITIC的设计初衷背道而驰了。所以我在实现里默认保留abs,但你如果复现某些文献,要先看清楚原文公式用的是哪种约定。

3.4 第4步:信息量合成与权重归一化

有了对比强度和冲突性,剩下就是简单的逐元素乘法和归一化:

% 信息量:对比强度 × 冲突性 C = sigma .* conflict; % 归一化权重 w = C ./ sum(C); % 打印权重 for i = 1:m fprintf('指标%d权重:%.4f\n', i, w(i)); end % 绘制权重柱状图 bar(w); set(gca, 'XTickLabel', {'指标1', '指标2', '指标3', '指标4'});

在MATLAB里,sigma是1×m的行向量,conflict也是1×m,用.*做逐元素相乘,不能写成sigma * conflict,那样会变成矩阵乘法直接报维度错误。C的值越大,说明这个指标既在样本间有区分度,又和其他指标信息重叠少,理所应当拿到更高的权重。

如果一切正常,输出结果大概是下面这种感觉(以4个指标为例):

指标标准差σ冲突性信息量C权重w
指标10.2412.7120.6530.312
指标20.1582.1850.3450.165
指标30.3321.8960.6290.301
指标40.1272.3400.2980.142

所有指标权重相加等于1。如果某个指标的C算出来是0,先回头检查这一列数据是不是所有样本取值都相同——方差为0,信息量为0,权重必然被打到0,这种指标要么剔除,要么换数据。

4. 跑通全程:环境清单、三步启动与结果解读

4.1 运行环境与文件清单

这套代码不依赖任何额外工具箱,纯基础MATLAB函数就能跑。版本方面,R2019a以上比较稳妥,因为readmatrix函数需要这个版本;如果你还在用R2016a这类老版本,把readmatrix改成xlsread就行,用法是data = xlsread('data_input.xlsx'),它会自动跳过文本行,直接返回数值矩阵。

完整文件清单如下:

文件类型说明
main_critic.m主脚本从读数据到输出权重的完整流程
critic_core.m函数CRITIC核心算法,一般不需要改动
normalize_minmax.m函数极差标准化实现
data_input.xlsx数据样例数据,运行时替换成你自己的数据

4.2 三步跑通:替换数据、执行脚本、查看输出

第一步是把数据整理成Excel表,每行是一个样本,每列是一个指标,第一行放指标名,第一列放样本名。如果你的指标里有成本型(越小越好),记下它的列号,打开main_critic.m,修改costCols这一行配置。注意,正向化必须放在标准化之前,顺序错了权重会直接失真。

第二步是在MATLAB里把当前路径切换到源码目录,命令行输入main_critic直接回车。脚本会自动读取data_input.xlsx,跑完会在命令行打印每个指标的权重,并弹出一张权重柱状图。工作区里会出现w、sigma、conflict、C几个变量,w就是最终权重向量。

第三步是检查结果。权重之和应该严格等于1,任一指标的权重不应出现负值,柱状图的高度应该和打印结果一致。如果发现某个权重特别小或特别大,不要急着怀疑代码,先回到数据检查那一列的分布情况。

4.3 结果怎么进一步用:算综合得分与灵敏度检查

CRITIC算出来的权重通常不是终点,后面还要做综合评价。最常见的用法是线性加权,直接用标准化后的矩阵乘权重向量得出每个样本的综合得分:

score = X_norm * w; % 加权综合得分 [~, idx] = sort(score, 'descend'); % 按得分降序排列

这样就能对样本做排序了。如果你的后续流程是TOPSIS,这个权重可以当成TOPSIS的输入权重直接使用。顺手做一次灵敏度检查能增加结果可信度:随机剔除一个样本重新跑一遍CRITIC,观察权重变化幅度。如果权重波动剧烈,说明样本量太少、结果不稳定,这时候CRITIC只能作为参考,最好配合其他方法交叉验证。

5. 常见问题与避坑记录:CRITIC实现路上的5个典型踩坑点

5.1 缺失值导致所有权重变成NaN

现象:运行脚本后,命令行打印的全是NaN,柱状图也是空白的。

原因:readmatrix读取Excel时,空白单元格会被自动填充为NaN。后续的min、max、std、corrcoef遇到NaN会一路传染,最终权重全部变成NaN。这不是算法问题,是数据预处理没做好。

解决:在正向化之前先对数据做缺失值清理,用rmmissing删除含缺失值的行,或者用fillmissing按列插补。我一般习惯把缺失值处理放在读入之后第一件事,优先级高于正向化。

5.2 指标方向没统一导致权重和直觉相反

现象:某个成本型指标(比如“故障率”)算出来的权重高得离谱,或者和领域经验完全相反。

原因:CRITIC是纯数据驱动的方法,它不知道哪个指标是“越小越好”。你如果直接拿原始数据喂进去,标准差和相关系数都不会区分方向,结果的解释方向可能正好反了。

解决:在标准化之前对成本型指标做正向化,推荐倒数变换。改完之后重新跑一遍,这个指标的相关性符号会翻转,权重分布才会回到正常范围。

5.3 相关系数的负号处理不一致

现象:同一个数据集,有人跑出来权重和你完全不一样,尤其是某些负相关指标差很多。

原因:冲突性的计算存在两种约定。一种直接用1 - R,负相关会得到大于1的冲突值;另一种用1 - abs(R),负相关被折减为低冲突。两种做法没有绝对的对错,但结果差异很大,代码里写的是哪一种,直接决定权重走向。

解决:先看你的参考资料用哪种公式。如果是自己用,建议统一用abs版本,理由很直接——指标的正负向编码有时是人为规定的,比如“正向化”可以取倒数也可以取负,编码方式一变,相关系数符号就变,用abs能保证结果不随编码方式漂移,复用性更强。

5.4 样本太少导致权重震荡

现象:只有三五个样本,删掉一个样本重新跑,权重排序完全变了,像玄学一样不稳定。

原因:相关系数矩阵对样本量非常敏感。样本量低于8个时,相关系数的置信区间很宽,算出来的冲突性随机性强,权重自然跟着抖。

解决:至少保证10个以上样本再谈客观赋权。实在凑不够样本,就把CRITIC和熵权法做加权融合,给两者各分配一个经验系数,这种带有主观修正的组合方式比单一方法更能抗小样本干扰。

5.5 和熵权法结果差异大,怀疑自己算错了

现象:用同一份数据跑熵权法和CRITIC,权重分布完全不同,甚至出现了“熵权法给A最高、CRITIC给A最低”的翻车局面。

原因:这通常不是代码错,而是两种方法的数学逻辑本质不同。熵权法只看指标离散程度;CRITIC还考虑了指标间相关性。如果指标体系里存在强相关指标,两个方法的结果差异会非常明显。

解决:按指标体系的性质选方法。指标间关联度低、相对独立时,熵权法就够了;指标间高度相关、想避免重复计权时,优先选CRITIC。做研究时可以把两种都跑出来,在论文里说明选择理由,这反而比只丢一个权重更有说服力。

6. 二次开发思路:把脚本一步步改成可复用工具包

6.1 第一步:从脚本到函数,加参数校验

脚本只能在固定路径下运行,换个数据就得打开代码改路径。我会先改成函数,输入纯数据矩阵,输出权重和信息量:

function [w, out] = criticWeight(X, opt) arguments X double opt.method char = 'abs' % 默认取绝对值 opt.normalize char = 'minmax' % 默认极差标准化 end % 正向化、标准化、计算权重 % 校验:X为空或含NaN直接报错 if isempty(X) || any(isnan(X), 'all') error('输入数据不能为空或包含NaN'); end % ... 核心计算与输出 end

这样main脚本只剩一行调用,其他项目也能直接import这个函数。参数校验是必须的,缺失值早暴露比晚暴露好。

6.2 第二步:支持多种冲突性变体

我看过一些改进思路,有人把相关系数换成协方差矩阵,有人在信息量里加了熵值修正,还有人研究过正负相关分开计算的情况。在函数里留一个method参数,用switch切分即可:

switch opt.method case 'abs' % 常用实现:取绝对值 conflict = sum(1 - abs(R), 1) - 1; case 'raw' % 原始公式:不取绝对值 conflict = sum(1 - R, 1) - 1; case 'entropy' % 引入熵值修正项 E = entropy(X_norm); conflict = sum(1 - abs(R), 1) - 1 + E; otherwise error('未知method'); end

每个分支对应不同的方法论倾向。做对比实验时,只需要切换method参数就能批量产出不同版本的结果,省去反复改主脚本的麻烦。

6.3 回归验证与习惯养成

改造完之后,最怕的一件事是“代码改着改着结果悄悄变了”。我每次改动后,都会用data_input.xlsx作为基准数据跑一遍,把权重结果和上一次的保存值做对比,差值超过1e-10就直接报红。自从有过一次改标准化方式导致权重全变的经历,我每次改完这套代码都会强制走一遍回归对比,把结果表存档。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询