首页 / 资讯中心 / 文章详情

前端实现AHP层次分析法:权重计算与一致性检验实战

前端实现AHP层次分析法:权重计算与一致性检验实战 ★ FEATURED ARTICLE
简介这是一款基于 JavaScript 实现的层次分析法AHP可视化工具面向需要借助结构化方法辅助个人或团队决策的学习者与开发者可用于梳理评价指标权重、比较方案优劣等场景。压缩包共 14 个文件约 476KB以 5 个 js 脚本承载核心计算与模块逻辑配合 2 个 json 配置、2 个 css 样式及 1 个 html 入口页面另附 license、notice、md 说明等文档整体结构轻量清晰便于本地运行与二次开发。目前已有 814 人学习下载。需要提醒的是该计算器可正常运行但暂未提供数据保存功能浏览器回退可能导致输入丢失作者建议同步在电子表格中记录数据并欢迎提交拉请求参与改进。借助该工具读者可直观理解 AHP 的层次建模与权重计算过程并基于现有模块结构进行功能扩展。1. 一个前端也能跑起来的 AHP 权重计算器做技术选型或者方案评审时最怕遇到“我觉得 A 好你觉得 B 好”的扯皮。层次分析法AHP就是用来终结这种扯皮的把模糊的主观判断通过两两比较和一致性检验量化成一组能写进评审报告的权重数字。ahp-tool是一个用 JavaScript 实现的 AHP 工具纯前端逻辑不依赖后端服务丢进浏览器或者 Node 环境就能跑。它解决的核心问题很具体——帮你把判断矩阵算成权重向量顺带告诉你这份判断靠不靠谱。适合谁写过 JS、需要在页面里集成决策模块的前端以及想快速验证一套指标权重是否合理的技术负责人。下面按“矩阵怎么建、权重怎么算、一致性怎么验、坑在哪”的顺序拆开讲。2. 判断矩阵怎么建从业务指标到 n×n 数字表AHP 的第一步不是写代码是把你要比较的东西列清楚。假设你在做一个模拟项目的前端框架选型候选方案有三个评价维度有四个开发效率、运行性能、生态成熟度、团队熟悉度。这四个维度就是准则层三个方案是方案层。ahp-tool处理的是准则层对目标的权重方案层对每个准则的权重可以复用同一套逻辑逐层算。2.1 标度选择1-9 标度为什么是默认值AHP 最常用的标度是 Saaty 的 1-9 整数标度含义是1 表示两个因素同等重要3 表示前者稍重要5 表示明显重要7 表示强烈重要9 表示极端重要2、4、6、8 是中间值。选 1-9 不是玄学是因为心理学实验表明人对“重要程度”的区分能力大致在 7±2 个等级超过这个范围判断就不可靠了。在ahp-tool里判断矩阵是一个二维数组matrix[i][j]表示因素 i 相对因素 j 的重要性。约定matrix[i][j] 1 / matrix[j][i]对角线恒为 1。这个倒数关系是后面一致性检验能成立的前提建矩阵时如果手填出错最容易翻车的就是这里。// 4 个准则的判断矩阵行/列顺序开发效率、运行性能、生态成熟度、团队熟悉度 const matrix [ [1, 3, 2, 5 ], // 开发效率 vs 其余 [1/3, 1, 1/2, 2 ], // 运行性能 vs 其余 [1/2, 2, 1, 3 ], // 生态成熟度 vs 其余 [1/5, 1/2, 1/3, 1 ] // 团队熟悉度 vs 其余 ];这段矩阵读法是开发效率比运行性能稍重要3比生态成熟度介于同等和稍重要之间2比团队熟悉度明显重要5。注意第 2 行第 1 列必须是 1/3不是 3方向反了整个矩阵就废了。我一般会先只填上三角下三角用代码自动取倒数生成减少手误。2.2 矩阵合法性检查三个必查项建完矩阵别急着算权重先过三道检查。第一维度是否方阵matrix.length matrix[0].length第二对角线是否全为 1第三是否满足互反性matrix[i][j] * matrix[j][i] ≈ 1。第三项用浮点比较容差给 1e-9 就够。function validateMatrix(m) { const n m.length; for (let i 0; i n; i) { if (m[i].length ! n) throw new Error(第 ${i} 行长度不等于 ${n}); if (Math.abs(m[i][i] - 1) 1e-9) throw new Error(对角线 [${i}][${i}] 不为 1); for (let j 0; j n; j) { if (Math.abs(m[i][j] * m[j][i] - 1) 1e-9) { throw new Error(互反性不满足: [${i}][${j}]${m[i][j]}, [${j}][${i}]${m[j][i]}); } } } return true; }参数说明容差 1e-9 是给浮点除法留的余量比如 1/3 乘 3 在 JS 里是 0.9999999999999999 而不是 1容差太小会误报。这个函数抛错而不是返回 false是为了让调用方在构建阶段就中断避免带着坏矩阵往下算。实际项目里我会把它挂在表单提交前用户改一个格子就重跑一次错误信息直接标到对应单元格。3. 权重向量的三种算法特征向量、几何平均、算术平均矩阵合法之后核心任务是把 n×n 矩阵压成一个长度为 n 的权重向量且各分量之和为 1。ahp-tool这类工具通常提供多种算法因为不同算法在精度和实现复杂度上各有取舍选哪个取决于你对结果精度的要求和运行环境。3.1 特征向量法精度最高但需要迭代严格意义上的 AHP 权重是判断矩阵最大特征值对应的特征向量。求特征向量常用幂迭代法随便取一个正向量反复用矩阵乘它并归一化直到收敛。function eigenVector(m, maxIter 1000, tol 1e-10) { const n m.length; let v new Array(n).fill(1 / n); // 初始均匀向量 for (let iter 0; iter maxIter; iter) { const next new Array(n).fill(0); for (let i 0; i n; i) { for (let j 0; j n; j) next[i] m[i][j] * v[j]; } const sum next.reduce((a, b) a b, 0); for (let i 0; i n; i) next[i] / sum; // 归一化 let diff 0; for (let i 0; i n; i) diff Math.abs(next[i] - v[i]); v next; if (diff tol) break; // 收敛退出 } return v; }逻辑说明每轮先做矩阵向量乘再归一化用相邻两轮向量的 L1 距离判断收敛。maxIter给 1000 是保险值正常 4 到 8 阶矩阵几十轮就收敛tol取 1e-10 是因为权重通常保留 4 位小数再高的精度对业务没有意义。这个方法的代价是每轮 O(n²)n 大了会慢但 AHP 的 n 一般不超过 9实际感受不到。3.2 几何平均法闭式解适合前端实时计算几何平均法也叫方根法不需要迭代直接对每行求几何平均再归一化公式是w_i (∏_j a_ij)^(1/n) / Σ_k (∏_j a_kj)^(1/n)。它的结果和特征向量法非常接近在一致性较好的矩阵上差异通常在小数点后第三位。function geometricMean(m) { const n m.length; const rowGeo m.map(row Math.pow(row.reduce((a, b) a * b, 1), 1 / n)); const sum rowGeo.reduce((a, b) a b, 0); return rowGeo.map(x x / sum); }这段代码短到可以内联好处是没有循环依赖、没有收敛判断用户拖动滑块改矩阵时能立刻出结果。我在页面里做实时预览时优先用它等用户点“确认”再跑一次特征向量法做最终值两者差异超过 0.01 就提示用户矩阵可能不一致。3.3 算术平均法最粗糙但最好解释算术平均法是按列归一化后对每行求平均实现最简单但精度最差一般只用来做教学演示或者快速估算。三种方法的对比如下算法时间复杂度精度是否需要迭代适用场景特征向量法O(n²·iter)最高是最终结果、评审报告几何平均法O(n²)接近特征向量否前端实时预览算术平均法O(n²)较低否教学、快速估算选型建议很直接要写进正式文档的权重走特征向量法交互过程中的即时反馈走几何平均法两者结果差太多说明矩阵本身有问题这时候该回头改矩阵而不是纠结算法。4. 一致性检验CR 值算不对权重就是自欺欺人AHP 最容易被忽略也最容易翻车的一步是一致性检验。人做两两比较时会出现逻辑矛盾比如 A 比 B 重要、B 比 C 重要结果又填 C 比 A 重要。一致性比率 CR 就是用来量化这种矛盾的CR 0.1 才算通过否则矩阵要重填。4.1 最大特征值与 CI 的计算一致性指标 CI (λ_max - n) / (n - 1)其中 λ_max 是最大特征值。用特征向量法时λ_max 可以用Aw / w的各分量平均来近似w 是归一化后的权重向量。function consistencyRatio(m, weights) { const n m.length; // 计算 Aw const Aw m.map(row row.reduce((s, val, j) s val * weights[j], 0)); // λ_max 近似各分量 (Aw)_i / w_i 的平均 const lambdaMax Aw.reduce((s, val, i) s val / weights[i], 0) / n; const CI (lambdaMax - n) / (n - 1); const RI [0, 0, 0.58, 0.90, 1.12, 1.24, 1.32, 1.41, 1.45][n]; // Saaty 随机一致性指标 return { lambdaMax, CI, CR: CI / RI }; }参数说明RI 是随机一致性指标查表得来n1、2 时 RI 为 0二阶矩阵永远一致CR 无意义n3 到 9 对应 0.58 到 1.45。这段代码里 RI 数组下标从 0 开始所以RI[n]直接取到对应阶数别写成RI[n-1]这是血泪经验错一位整个检验就废了。返回的 CR 小于 0.1 通过0.1 到 0.2 之间属于边缘超过 0.2 必须重填。4.2 CR 超标时怎么改矩阵CR 超标不是让你随便调数字凑通过而是要找矛盾最大的那一对比较。做法是对每个三元组 (i, j, k)检查a_ij * a_jk和a_ik的偏离程度偏离最大的那个格子就是矛盾源头。常见做法是先把偏离最大的a_ik往a_ij * a_jk的方向调再重算 CR反复两三轮基本能压到 0.1 以下。function findWorstTriple(m) { const n m.length; let worst { i: -1, j: -1, k: -1, dev: 0 }; for (let i 0; i n; i) for (let j 0; j n; j) for (let k 0; k n; k) { if (i j || j k || i k) continue; const expected m[i][j] * m[j][k]; const dev Math.abs(Math.log(m[i][k] / expected)); // 用对数比衡量偏离 if (dev worst.dev) worst { i, j, k, dev }; } return worst; }用对数比而不是绝对差是因为判断矩阵里的值跨越 1/9 到 9绝对差对大小值不公平。返回的三元组直接告诉用户“你对 i 和 k 的判断和你对 i-j、j-k 的判断矛盾最大”比干巴巴一个 CR 数字有用得多。这个函数在 n9 时是 729 次循环完全扛得住。5. 避坑清单五个让权重算错的真实原因5.1 现象权重加起来不等于 1差一点点原因归一化时用了toFixed或者手动四舍五入导致精度丢失。解决归一化只在最后一步做中间过程保留全精度展示时再格式化。如果业务要求权重和严格为 1把舍入误差补到最大分量上。5.2 现象CR 算出来是 NaN原因权重向量里有 0导致Aw[i] / weights[i]除零。解决判断矩阵是正互反矩阵理论上权重不会为 0出现 0 说明矩阵里有 0 或负数先跑validateMatrix拦住。5.3 现象二阶矩阵 CR 报错或报警原因n2 时 RI0CI 也为 00/0 得 NaN。解决n≤2 直接跳过一致性检验返回 CR0二阶矩阵天然一致不需要验。5.4 现象几何平均法和特征向量法结果差很多原因矩阵一致性差两种算法对不一致矩阵的敏感度不同。解决这不是算法 bug是矩阵有问题的信号先看 CRCR 超标就改矩阵别在算法上找原因。5.5 现象用户改了矩阵页面权重没更新原因矩阵是引用传递改了内层数组但没触发重算或者缓存了旧权重。解决每次计算前深拷贝矩阵或者用不可变更新计算函数保持纯函数输入变则输出必变。6. 把 AHP 嵌进表单一个可复用的计算钩子真正落地时AHP 不是一个独立脚本而是要嵌进表单里跟着用户输入实时算。我一般会封装一个计算钩子把校验、权重、一致性检验串成一条流水线返回结构化结果供 UI 消费。function computeAHP(matrix) { validateMatrix(matrix); const weights eigenVector(matrix); const { lambdaMax, CI, CR } consistencyRatio(matrix, weights); return { weights: weights.map(w Number(w.toFixed(4))), lambdaMax: Number(lambdaMax.toFixed(4)), CI: Number(CI.toFixed(4)), CR: Number(CR.toFixed(4)), passed: CR 0.1 }; } // 调用示例 const result computeAHP(matrix); console.log(result.weights); // [0.4824, 0.1571, 0.2720, 0.0885] console.log(result.CR); // 0.0162通过这个函数是纯函数输入矩阵输出结果不碰 DOM 也不碰全局状态方便单测。passed字段直接给 UI 用通过显示绿色不通过显示黄色并附上findWorstTriple的结果引导用户修改。权重保留 4 位小数是评审报告的常见精度再高没有业务意义。验证方法上我习惯用两个极端矩阵做回归全 1 矩阵应该输出均匀权重且 CR0完全一致的矩阵比如每行都是第一行的倍数应该输出和第一行几何平均一致的权重且 CR 接近 0。这两个用例能挡住大部分实现错误。从那以后我每次集成 AHP 模块都强制先跑这两个回归用例再联调 UI省得在页面上对着一个 NaN 猜半天。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站