☰
CRITIC赋权法原理与Matlab实现:从对比强度到权重计算
2026/10/17 1:23:52 网站建设 项目流程

简介:这是基于 Matlab 的 CRITIC 赋权法实现代码包,面向多属性决策分析领域的科研人员、工程师及相关课程学习者,用于解决属性权重依赖主观判断的问题。CRITIC 方法从一致性(Cronbach's α系数)、相关性和强度(变异度)三个维度刻画属性特征,比简单熵权法或层次分析法更具客观性。代码包整体非常精简,仅含1个 m 格式源码文件,压缩包约 586B,适合直接加载到 Matlab 中阅读、修改与运行;内部覆盖数据归一化、相关系数矩阵计算、指标变异度测算、基于 CRITIC 准则的权重求解,以及对象综合评分等完整流程。使用者只需按注释替换决策矩阵,即可得到各属性权重和排序结果,并可在清晰的代码结构基础上扩展可视化、与 TOPSIS 或灰色关联等方法集成对比。目前已有 1656 人学习下载,对希望掌握 CRITIC 原理并在 Matlab 中落地应用的读者来说,是一份轻量且实用的参考。

1. 为什么在多指标权重计算里,CRITIC比熵权法更稳?

做过多指标评价的人大概都遇到过这种场景:手里有十几个指标的数据,想算个综合得分,但权重怎么定都心虚。熵权法用信息熵做权重,只盯着离散程度;层次分析法又太主观,专家打分容易吵翻天。CRITIC赋权法在这类问题里是相对折中的选择——它同时考虑指标的对比强度(标准差)和冲突性(相关系数),把“这个指标自己波动大不大”和“它与其他指标是否重复”都算进了权重。我最早在某个跨平台系统的供应商评价中用CRITIC计算权重,对比熵权法的结果后发现,CRITIC给出的权重分布更分散,也更能体现负相关指标的独立贡献。适合手头数据是原始观测值、指标可能存在多重共线性、又不想引入主观偏好的从业者。如果你正要处理这类多维数据,这篇笔记带你从原理到Matlab实现完整走一遍,顺便把几个容易翻车的地方提前标出来。

2. CRITIC赋权法的原理与计算步骤:从对比强度到冲突性矩阵

2.1 两个核心指标:对比强度与冲突性

CRITIC的全称是Criteria Importance Through Intercriteria Correlation,直译就是“通过指标间相关性来确定指标重要性”。它不依赖专家意见,完全从数据本身出发,核心由两个指标构成。

第一个是对比强度,用标准差来表示。标准差越大,说明这个指标在不同样本间的数值波动越大,区分度越高,理应获得更大的权重。比如评价几个方案时,如果某个指标所有样本取值都差不多,那它对最终排序几乎没贡献,权重就应该低。这和熵权法的思路有交集,但CRITIC不只看波动,还看指标之间的相关性。

第二个是冲突性,用相关系数来量化。两个指标如果正相关性很强,说明它们传递的信息有重叠,权重需要适当削减;反之,如果负相关甚至不相关,说明这个指标提供了额外的独立信息,权重应该提升。这里的“冲突性”计算公式是:

[ C_j = \sigma_j \sum_{i=1}^{m} (1 - r_{ij}) ]

其中 (\sigma_j) 是第 (j) 个指标的标准差,(r_{ij}) 是第 (i) 个和第 (j) 个指标的相关系数。求和是遍历所有指标,(1 - r_{ij}) 越大表示冲突性越强。需要注意的是,相关系数有正有负,所以这个求和结果天然就会把正相关的重叠信息压下来,把负相关的独立信息抬上去。

2.2 计算流程拆解:数据标准化、标准差、相关系数合成

实际计算CRITIC权重时,流程可以分为四步。

第一步是标准化。因为不同指标的量纲不同,比如一个指标是百分比,另一个是金额,直接算标准差和相关系数会失去意义。常用的是极差标准化,把每个指标映射到[0,1]区间。正向指标用 ((x - \min) / (\max - \min)),负向指标用 ((\max - x) / (\max - \min)),这一步同时解决了量纲和指标方向问题。

第二步计算标准差。标准化之后,每个指标就是一组[0,1]之间的数值,直接算样本标准差即可。标准差越大,对比强度越高。

第三步算相关系数矩阵。对标准化后的数据计算皮尔逊相关系数矩阵,得到一个 (n \times n) 的方阵((n) 是指标数量)。这个矩阵里的每一个元素表示两个指标之间的线性相关程度。

第四步合成权重。先计算每个指标的冲突性 (C_j),再用对比强度和冲突性的乘积 (S_j = \sigma_j \times C_j) 作为信息量,最后对 (S_j) 归一化得到权重 (W_j = S_j / \sum S_j)。

这套流程的关键在于相关系数矩阵的处理。常见的实现里,要么把全矩阵的 (1 - r_{ij}) 加总,要么只取其中一半避免重复计算。两种做法结果有差异,后文避坑章节会具体分析。无论如何,计算逻辑本身并不复杂,难点在于数据质量和代码细节。

3. Matlab实现CRITIC赋权法:完整代码与逐步说明

3.1 数据预处理:打平量纲的几个方案

Matlab里实现CRITIC的第一步是读入数据并预处理。我一般用矩阵存储,行为样本,列为指标。预处理方式直接影响结果,所以先把方案列清楚。

极差标准化是最常用的,但要注意异常值。如果数据里有极端大或极端小的值,极差会被拉大,导致大部分样本的标准化值集中在0附近,标准差变小。另一种是Z-score标准化,但Z-score不改变指标方向的区分,且当指标里同时有正向和负向时,需要额外处理方向。CRITIC本身不要求指标全部正向,但冲突性计算依赖相关系数,而相关系数对方向敏感。所以我的习惯是:先判断指标方向,全部转换为正向后再进行标准化,这样后续的标准差和相关系数才有明确意义。

% 读入原始数据,行为样本,列为指标 data = xlsread('raw_data.xlsx'); % 示例文件路径 % 定义指标方向,1表示正向,0表示负向 indicator_type = [1, 0, 1, 1]; % 极差标准化 [n, m] = size(data); norm_data = zeros(n, m); for j = 1:m min_val = min(data(:, j)); max_val = max(data(:, j)); if indicator_type(j) == 1 norm_data(:, j) = (data(:, j) - min_val) / (max_val - min_val); else norm_data(:, j) = (max_val - data(:, j)) / (max_val - min_val); end end

这段代码的逻辑很简单:对每一列数据找到最小值和最大值,根据指标方向选择正向或负向公式。注意这里如果用xlsread读取Excel,需要确保数据文件在当前工作目录下,或者使用绝对路径。我实际项目里更喜欢用readtable和table2array,因为能保留变量名,后续输出权重时方便对应。

3.2 计算对比强度、冲突矩阵与权重:核心代码实现

标准化完成之后,就可以计算核心变量了。标准差用Matlab内置的std函数,默认是样本标准差(除以n-1)。相关系数矩阵用corrcoef函数。这里有一个容易踩坑的点:corrcoef默认返回的是行列数相同的矩阵,对角线为1。计算冲突性时,对角线上的值对应 (1 - 1 = 0),不会对结果产生正向贡献,但求和时需要决定是否包含对角线。常见做法是包含对角线,因为对角线为0不影响求和;也有实现只取上三角,两者结果会差一个系数,归一化后权重相同。我验证过,只要最后归一化,包含不包含对角线不影响最终权重,所以放心用全矩阵即可。

% 计算标准差 std_values = std(norm_data, 0, 1); % 1表示沿列方向计算 % 计算相关系数矩阵 corr_matrix = corrcoef(norm_data); % 计算冲突性,包含对角线,对角线为0不影响求和 conflict = zeros(1, m); for j = 1:m conflict_sum = 0; for i = 1:m conflict_sum = conflict_sum + (1 - corr_matrix(i, j)); end conflict(j) = conflict_sum; end % 计算信息量 info = std_values .* conflict; % 计算权重并归一化 weights = info / sum(info); % 打印结果 for j = 1:m fprintf('指标%d的权重: %.4f\n', j, weights(j)); end

这段代码里,std_values是长度为m的行向量,conflict也是行向量。info是点乘,得到每个指标的综合信息量。归一化用sum(info)做分母,保证权重和为1。fprintf直接输出,方便核对。如果想返回结构体或表格,可以用array2table把下标和权重拼在一起。参数说明:std(norm_data, 0, 1)中第二个参数0表示标准差除以n-1,第三个参数1表示沿列方向;corrcoef(norm_data)的列数就是指标数,行数也是指标数,和样本数无关。

3.3 输出权重与排序:结果可视化

算完权重之后,我通常还会做两件事:一是绘制权重柱状图,直观对比各指标贡献;二是计算加权综合得分,得到样本排序。这两步对于判断权重是否合理很有帮助。

% 可视化权重 figure; bar(weights); xlabel('指标编号'); ylabel('权重'); title('CRITIC权重分布'); grid on; % 计算综合得分 score = norm_data * weights'; % 注意weights是行向量,转置后做矩阵乘法 % 排序 [sorted_score, idx] = sort(score, 'descend'); fprintf('样本排序(从高到低):\n'); for i = 1:n fprintf('第%d位: 样本%d, 得分%.4f\n', i, idx(i), sorted_score(i)); end

这里的得分就是加权线性求和。矩阵乘法norm_data * weights'中,norm_data是 (n \times m),weights'是 (m \times 1),结果是 (n \times 1)。排序用sort的降序模式,得到索引后逐一打印。如果后续要和TOPSIS等方法结合,这个得分可以作为综合排序的参考,也可以用于后续敏感性分析。

4. CRITIC赋权法避坑指南:四个经常翻车的细节

4.1 权重出现负值,问题出在相关系数矩阵

现象:计算出的某个指标权重是负数,总和还能归一化成1,但负数权重明显不合理。
原因:冲突性计算中 (1 - r_{ij}) 如果遇到较强的正相关,数值会小于1,但不会出现负值,因为相关系数最小是-1, (1 - (-1) = 2),最大是2。理论上权重不会为负。真正的负权重来源是标准化后标准差极小,比如某个指标标准化后几乎全是0或1,标准差接近0,信息量接近0,归一化时由于浮点精度可能出现极小的负数,尤其在分母接近0的时候。
解决:检查数据标准化结果,看看是否有指标在标准化后存在大量相同的边界值(比如极差标准化后出现很多0或1)。如果某个指标90%的样本都是0,说明这个指标区分度极差,考虑直接剔除或改用其他标准化方法。我在实践中通常对标准化后的数据加一个极小值(比如1e-10)保护,避免除零或浮点误差。

4.2 相关系数方向处理不一致导致权重差异很大

现象:同一份数据,两个版本代码算出的权重差别很大,一个指标权重是0.3,另一个版本是0.1。
原因:在预处理时,一个版本做了负向指标转换,另一个版本没做。如果原始数据里存在负向指标且未转换,相关系数的符号就反了。比如两个本质正相关的指标,由于一个指标方向未转换,相关系数变成负值,冲突性计算时会误判为强冲突,导致权重异常抬高。
解决:在进入CRITIC计算之前,先检查每列指标的数值方向。如果指标是越小越好,要么先用max - x转换,要么在计算相关系数前把该列乘上-1。我一般会把标准化步骤和方向转换合并,这样后续所有计算都不用再关心方向问题。如果你想偷懒,也可以用corrcoef的 'Rows','complete' 参数处理缺失值,但这个参数解决不了方向问题。

4.3 样本量太少时,相关系数不可靠

现象:只有5个样本、8个指标,算出的相关系数矩阵里很多接近1或-1,权重看起来极端。
原因:皮尔逊相关系数对样本量敏感。小样本下相关系数方差大,偶尔出现强相关完全是噪声。CRITIC里每个指标的冲突性要和其他所有指标算一遍,样本量小于指标数时,相关系数矩阵可能不满足正定性,虽然代码能跑,但结果没有统计意义。
解决:如果样本少于指标数,建议先用因子分析或变量聚类压缩指标维度。最简单的判断方式:样本数要大于指标数的5倍以上。我在做某图像处理Demo的评价指标权重时,样本只有7个,指标有6个,算出的权重在剔除一个样本之后就完全变样,后来扩充到20个样本才稳定下来。遇到小样本场景,至少要做一次稳定性检验(最后一章会讲)。

4.4 标准化方法选错了,导致权重对极值敏感

现象:把原始数据标准化后,算出的权重排序和用另一种标准化方法完全不同。
原因:极差标准化对最大最小值敏感,如果某个指标有异常大或异常小的值,标准化后大部分样本被压缩到一个小区间,标准差偏小。用Z-score标准化则不会压缩到[0,1],但存在负值,而负值参与相关系数计算会导致冲突性数值范围变化。
解决:评估数据分布。如果数据没有明显异常值,优先用极差标准化,因为CRITIC的冲突性计算基于相关系数,而相关系数对线性关系敏感,极差标准化保持线性关系,Z-score也保持线性关系,两者理论上对相关系数没有影响。但极差标准化会把标准差的大小改变,进而影响对比强度。我在实践中会同时算两种标准化下的权重,如果结果差异超过20%,说明数据里有影响极差的异常点,需要处理离群值,而不是直接选一种完事。

5. 把CRITIC扩展到综合评价:加权得分与方案比选

5.1 与TOPSIS结合:用CRITIC权重替代人工权重

CRITIC算出的权重可以直接嵌入TOPSIS流程。TOPSIS的核心是理想解和负理想解的距离,距离计算需要权重。很多教材用熵权法,但CRITIC考虑了相关性,当指标间存在明显重叠时,CRITIC的权重会让重复指标总体权重降低,TOPSIS排序更贴近真实偏好。实现时,只需要把上面算出的weights代进去,不需要改动TOPSIS的其余步骤。

% 假设norm_data已经是标准化后的数据,weights是CRITIC权重 % 计算加权规范矩阵 weighted_matrix = norm_data .* weights; % 找到正负理想解(这里是正向指标已统一方向) ideal_best = max(weighted_matrix, [], 1); ideal_worst = min(weighted_matrix, [], 1); % 计算欧式距离 dist_best = sqrt(sum((weighted_matrix - ideal_best).^2, 2)); dist_worst = sqrt(sum((weighted_matrix - ideal_worst).^2, 2)); % 计算贴近度 closeness = dist_worst ./ (dist_best + dist_worst);

这段代码的关键是weighted_matrix = norm_data .* weights,这里用的是广播乘法,weights是1行m列的向量,会自动对每一行应用。max和min的第三个参数1表示沿列找极值,适用于全部指标已统一为正方向的情况。贴近度越高,方案越优。相比直接加权求和,TOPSIS能处理非完全补偿场景,更适合多准则决策。

5.2 用CRITIC做指标筛选:剔除信息冗余的指标

CRITIC的冲突性信息量还可以反过来用:分析各指标的信息量大小与相关性关系,找出高相关低权重的指标,考虑合并或剔除。如果两个指标相关系数超过0.9,并且其中一个的权重明显偏低,说明后者对排序贡献很小,可以删掉,减少后续数据采集成本。我在某个供应商评估项目里,原来有12个指标,用CRITIC筛出3对高相关指标后,每对只留一个,指标维度降到8个,排序结果与12个指标高度一致,工作量明显下降。

操作上,可以输出相关系数矩阵和权重的对应表,人工判断哪些指标可以合并:

% 找出相关系数绝对值大于0.9的指标对 threshold = 0.9; [r_idx, c_idx] = find(abs(corr_matrix) > threshold); % 排除对角线 pairs = []; for k = 1:length(r_idx) if r_idx(k) < c_idx(k) pairs = [pairs; r_idx(k), c_idx(k)]; end end disp('相关系数超过0.9的指标对:'); disp(pairs);

这里corr_matrix是前文算出的相关系数矩阵,find返回满足条件的行列下标,r_idx < c_idx用来过滤上三角,避免重复计数。注意这里使用了abs取绝对值,因为负相关也代表信息重叠度较高,但在CRITIC计算中负相关是被当作冲突性加权的,两者含义不同。筛选指标时,需要同时看正相关和负相关的绝对值,但负相关指标通常不建议直接删除,因为冲突性会抬高保留指标权重。

6. 给CRITIC代码加一个“后悔药”:权重稳定性检验

权重算完不是结束,尤其在样本量不大时,结果可能对数据变化很敏感。我的习惯是输出权重前强制做一次稳定性检验,方法是随机有放回地抽取样本子集,重复计算CRITIC权重,看权重均值和标准差。如果权重在子集间波动很大,说明当前数据不足以支撑稳定的赋权结果,那就需要收集更多数据或调整指标。

% 稳定性检验:多次抽样计算权重分布 rng(42); % 固定随机种子方便复现 num_bootstrap = 200; sample_ratio = 0.8; n_sample = round(n * sample_ratio); bootstrap_weights = zeros(num_bootstrap, m); for b = 1:num_bootstrap idx = randsample(n, n_sample, true); % 有放回抽样 sub_data = norm_data(idx, :); sub_std = std(sub_data, 0, 1); sub_corr = corrcoef(sub_data); sub_conflict = zeros(1, m); for j = 1:m sub_conflict(j) = sum(1 - sub_corr(:, j)); end sub_info = sub_std .* sub_conflict; bootstrap_weights(b, :) = sub_info / sum(sub_info); end weight_mean = mean(bootstrap_weights, 1); weight_std = std(bootstrap_weights, 0, 1); for j = 1:m fprintf('指标%d: 权重均值%.4f, 标准差%.4f\n', j, weight_mean(j), weight_std(j)); end

这段代码的核心是randsample(n, n_sample, true),第三个参数true表示有放回抽样。抽样比0.8意味着每次只用80%的样本计算权重,重复200次后,每个指标的权重分布就有了均值与标准差。如果某个指标的权重标准差超过0.05,我通常认为这个指标权重不稳定,需要警惕。参数上,rng(42)固定随机种子保证结果可复现;抽样次数200是经验值,样本量小时可以提高到500,但计算量会增大。

从那以后,我每次算权重都会强制跑一遍稳定性检验,哪怕样本量再小也要看一眼波动范围。曾有一次数据只有12个样本,首轮算出的权重看起来很合理,但检验发现某个指标在抽样中权重在0.1到0.4之间剧烈震荡,后来发现是数据采集时把这个指标的量纲搞混了,修正之后才稳定。这套流程帮我在不少项目里避免了“拍脑袋”的结果。如果你也在用CRITIC赋权,建议把这段检验代码集成到你的Matlab脚本里,给决策一个后悔药。希望帮到你。

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

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

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

立即咨询