资讯详情 模糊综合评判Python实现:隶属度矩阵、AHP熵权法与合成算子
📅 2026/10/24 4:38:13
简介模糊综合评判方法的原理与 Python 实现构成这份资源的主线面向需要处理不确定性评价问题的学生、研究者与工程实践者。内容从因素集、评价等级集、权重确定、隶属度向量到模糊评判矩阵的构建再到模糊合成运算与结果归一化覆盖了完整的方法链路可用于项目评审、方案比选、绩效评估等场景。压缩包共 3 个文件包含 1 份讲解合成算子选取的 PDF 文献、1 份 Python 代码文档与 1 个 fuzzy.py 脚本整体约 250KB轻量易取便于边读原理边对照代码验证。已有 3621 人学习下载。读者可借助文献理解不同合成算子对评判结果的影响再通过脚本把权重计算、矩阵构造与归一化流程跑通直接改为自己的指标体系与隶属函数减少从公式到可运行代码的摸索成本。1. 选型评审里被平均分掩盖的短板用隶属度矩阵摊开给一张评审表收尾最怕遇到这种情况某个方案在稳定性上明显拉胯但因为其他几项分数高加权平均一摊总分还是排第一。传统加权求和把“某一项很差”这个信息稀释掉了评委心里有数报告上却看不出。模糊综合评判方法python这套代码要解决的就是这件事——它先把每个指标对“优、良、中、差”的隶属程度算成一个矩阵再用权重做合成输出的是覆盖整个评语集的隶属向量而不是一个孤零零的分数。zip 里是一套可直接跑的 Python 实现覆盖隶属函数、AHP 与熵权法定权、四种合成算子和多级评判。适合手里一堆定性指标、又必须给出可解释结论的人。2. 因素集、评语集与隶属度矩阵三个集合定错后面全白算模糊综合评判的全部计算都压在一个矩阵 R 上R 的每一行对应一个指标每一列对应一个评语等级元素 r_ij 表示“第 i 个指标被评为第 j 等的程度”。这个矩阵一旦定歪后面权重再精细、算子再花哨结论都是玄学。所以真正该花时间的是前面这三件事因素集 U 里放什么、评语集 V 分几档、隶属度怎么从原始数据算出来。2.1 因素集 U 和评语集 V 的粒度怎么定因素集 U 就是参与评判的指标集合通常来自一张现成的指标体系表比如技术方案评审里的“功能覆盖度、性能余量、稳定性、可维护性、交付周期、报价”。这里最常见的翻车是没有检查指标方向——报价、交付周期、缺陷率这些是负向指标数值越大评价越差如果直接把原始数值扔进隶属函数算出来的 R 会把“报价高”判成“优”。我在实际项目里一般会先给每个指标打上方向标签正向越大越好、负向越小越好、区间型落在某段最好比如响应时间 200400ms。方向不统一就没法放到同一个评语集下讨论。评语集 V 是评价等级的集合工程上最常用的是奇数级3 级、5 级、7 级。级数不是越多越好评语级数一多隶属度矩阵的行会变得稀疏相邻等级的区分度下降最后靠最大隶属度原则挑出来的那一档很可能只是数值噪声级数太少比如只有“合格/不合格”又没法体现梯度评审结论会变成二值判断。我的经验是5 级优、良、中、较差、差能覆盖大部分评审场景7 级适合需要精细区分的绩效考核3 级只适合初筛。定级数的时候还要同时定锚点也就是每个等级对应的分数中心比如 95/85/75/65/50锚点直接决定后面的隶属度计算锚点之间等距是最省事也最容易解释的做法。因素集的另一个坑是层级。当指标超过 8 个单级评判会因为权重被稀释而失去区分度这时候应该先分组比如把“功能、性能、稳定性”归到技术维度“报价、周期、付款条件”归到商务维度先做二级评判再做一级合成。这件事在代码里体现为两套权重、两次合成不是把 12 个指标硬塞进一个 12 维的 A 向量。2.2 隶属函数的选型三角、梯形、高斯分别管哪类指标隶属函数负责把“指标值”翻译成“隶属度”选型取决于指标的物理含义和数据形态。下面这张表是我这几年反复用下来的一张对照基本能覆盖常见场景。函数类型参数适用指标注意点三角形(a, b, c) 左底、顶点、右底专家打分、等级明确的定性指标顶点处隶属度为 1两侧线性衰减解释成本最低梯形(a, b, c, d) 两底两顶存在“合适区间”的指标如温度、库存量平台区宽度不能拍脑袋要来自业务约束高斯型(c, σ) 中心、宽度测量噪声大、连续性强的物理量σ 选小会让矩阵接近 0/1选大则区分度消失分段线性一组断点有国标/行标分级的指标断点必须来自标准文本不能自创选型的判断标准其实只有一条这个指标的“优”是一个点还是一个区间如果是一个点用三角形或高斯型如果一个区间都算优用梯形。很多教程默认全用三角形这在指标含有区间约束时会系统性地低估隶属度——比如“CPU 占用率 40%60% 都算合理”用单峰三角形就会把 50% 和 60% 判出明显差距而实际上业务上认为它们一样好。还有一个容易被忽略的细节是隶属函数的“腰宽”。以三角形为例顶点在锚点 b左右底在 b±halfhalf 的取值直接决定相邻等级的覆盖范围。half 取相邻锚点间距的一半能保证相邻三角形在中间点恰好相交、隶属度各为 0.5这是最不容易出现“某一行全零”的取法。如果指标分布明显偏斜比如大部分方案都集中在 80 分以上可以考虑对 half 做非对称处理但一定要在报告里写清楚否则复现的人对不上数。提示不要把隶属函数当成调参旋钮。换一个 half 就能让结论翻转说明指标体系本身没有区分度这时候该回去改指标而不是继续调函数。2.3 从专家打分表到 R 矩阵的代码落地有了锚点和函数R 矩阵的计算就是逐指标、逐等级地算隶属度再按行归一化。下面这段是包里最核心的一段也是我每次接手新项目时最先改的地方——把samples换成真实数据把half换成业务认可的腰宽其他逻辑基本不动。import numpy as np # 评语集锚点优 95 / 良 80 / 中 65 / 差 45级数和分数都按项目改 ANCHORS np.array([95.0, 80.0, 65.0, 45.0]) def tri_mf(x, a, b, c): 三角形隶属函数a 左底b 顶点c 右底 if x a or x c: return 0.0 if x b: return 1.0 return (x - a) / (b - a) if x b else (c - x) / (c - b) def build_R(samples, anchorsANCHORS, half8.0): samples: {指标名: [专家1打分, 专家2打分, ...]} half: 相邻锚点间距的一半控制隶属函数的腰宽 rows [] for name, scores in samples.items(): row [] for v in anchors: # 每个专家在该等级上的隶属度取均值等价于专家组投票 row.append(np.mean([tri_mf(s, v - half, v, v half) for s in scores])) row np.array(row, dtypefloat) # 行归一化保证每行隶属度之和为 1全零行退化为均匀分布 if row.sum() 1e-12: row row / row.sum() else: row np.full(len(anchors), 1.0 / len(anchors)) rows.append(row) return np.array(rows) samples { 功能覆盖度: [92, 88, 90, 85], 性能余量: [78, 82, 75, 80], 稳定性: [55, 60, 58, 62], 可维护性: [70, 72, 68, 74], } R build_R(samples) print(np.round(R, 3))这段代码的逻辑分三步。第一步对每个指标、每个评语锚点把该指标下所有专家的打分逐一代入三角形函数得到单个专家的隶属度第二步对专家求均值这一步等价于“专家组投票”也是把主观打分变成模糊关系的关键——它不要求专家先商量出统一分数只要求他们在同一把尺子上打分第三步按行归一化强制每行隶属度之和为 1避免某些指标因为落在函数重叠区导致整行和偏离 1影响后续合成。参数说明anchors必须和评语集级数严格对应改级数时同步改这里否则会出现维度对不上half建议按相邻锚点间距的一半起步本例锚点间距 15half≈7.5取 8 是留一点重叠如果指标分布集中在少数几档可以适当放大 half 让分布摊开但放大后一定要看每行是否出现多个接近的峰值那意味着等级区分度不够samples的每个列表长度可以不相等但同一指标下专家数量太悬殊会让均值失真常见做法是缺项专家不参与该指标的均值计算而不是补一个默认分。跑完之后我一般会做一件小事把 R 打印出来逐行看。如果某一行四个数都在 0.25 附近说明这个指标在专家组内部争议极大或者锚点覆盖不到得回去核对打分尺度如果某一行有一项超过 0.9说明该指标的评价高度一致这一项在合成里会起主导作用权重上要留意别让它被过度放大。这一步花不了几分钟却能拦住后面 80% 的“结果看着怪但说不清哪里怪”。3. 权重从哪来AHP 一致性检验与熵权法的分工R 矩阵只描述了“每个指标被评成各等级的程度”还缺一个东西回答“哪个指标更重要”这就是权重向量 A。工程上权重来源基本两条路一条是主观定权用 AHP 判断矩阵把专家偏好结构化另一条是客观定权用熵权法从数据本身的离散程度反推。两条路各有各的失效场景混用之前得先搞清楚它们分别在测什么。3.1 AHP判断矩阵不是拍脑袋填的AHP 的核心是一张两两比较矩阵元素 a_ij 表示“指标 i 相对指标 j 的重要程度”常用 19 标度。填完之后求最大特征值对应的特征向量归一化就是权重。AHP 真正有价值的地方不是那个特征向量而是一致性检验——它逼着你检查自己填的比较关系有没有自相矛盾比如 A 比 B 重要、B 比 C 重要结果 C 又比 A 重要这种循环在多人会签的场景里非常常见。import numpy as np # 随机一致性指标 RI按矩阵阶数取1~9 阶是标准表 RI {1: 0.0, 2: 0.0, 3: 0.58, 4: 0.90, 5: 1.12, 6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45} def ahp_weights(A): A: 正互反判断矩阵a_ij 0 且 a_ji 1 / a_ij A np.asarray(A, dtypefloat) n A.shape[0] eigvals, eigvecs np.linalg.eig(A) k int(np.argmax(eigvals.real)) lam eigvals[k].real w np.abs(eigvecs[:, k].real) w w / w.sum() # 特征向量归一化即权重 CI (lam - n) / (n - 1) if n 1 else 0.0 CR CI / RI[n] if RI.get(n, 0) 0 else 0.0 return w, lam, CI, CR A np.array([ [1, 3, 2, 4], [1/3, 1, 1/2, 2], [1/2, 2, 1, 3], [1/4, 1/2, 1/3, 1], ]) w, lam, CI, CR ahp_weights(A) print(权重:, np.round(w, 3)) print(lambda_max:, round(lam, 4), CI:, round(CI, 4), CR:, round(CR, 4))代码里RI表是查表用的n是矩阵阶数超过 9 阶时标准 RI 表不再可靠这种规模通常应该改用 Delphi 法加分组而不是硬算。lam是最大特征值的实部理论上应该等于 n实际会略大CI是偏离程度CR CI / RI是相对偏离工程上一般要求 CR 0.1严格一点的场景要求 0.05。CR 超标时不要用“取平均”或者“强行归一化”绕过正确的做法是回到矩阵找出矛盾的三元组请专家重新比较那一对。注意特征向量法在接近一致的矩阵上表现稳定但对舍入误差敏感。填矩阵时用 1/3 而不是 0.33用整除形式表达倒数能让复现的人拿到一致的结果。3.2 熵权法让数据自己说话但它不认“重要性”熵权法的出发点是某个指标在各方案之间取值差异越大说明它携带的区分信息越多权重应该越高如果所有方案在该指标上几乎一样那这个指标对排序没有贡献权重就该低。这个逻辑和 AHP 完全不同——熵权法衡量的是“区分度”不是“业务重要性”。一个指标可能业务上极其关键但如果所有候选方案在这项上都差不多熵权法会给它很低的权重这在评审场景里往往是反直觉的。def entropy_weights(X): X: m 个方案 × n 个指标 的原始矩阵传入前必须已完成同向化 X np.asarray(X, dtypefloat) m, n X.shape # 极差标准化把不同量纲压到 [0, 1] Xn (X - X.min(axis0)) / (X.max(axis0) - X.min(axis0) 1e-12) # 平移一个极小量避免 log(0) P (Xn 1e-12) / (Xn 1e-12).sum(axis0, keepdimsTrue) k 1.0 / np.log(m) e -k * (P * np.log(P)).sum(axis0) # 各指标的信息熵 d 1 - e # 差异系数 return d / d.sum()参数说明X的行是方案、列是指标方向必须统一否则标准化之后负向指标会反向拉高权重m是方案数熵权法在 m 很小比如只有 23 个方案时极不稳定这时候权重的意义接近于噪声建议改用 AHP 或等权k 1/ln(m)是让信息熵落在 [0,1] 的归一化系数1e-12是为了防零不能省略指标某一列全相同时分子分母都会归零这时候熵权法给出的该列权重应该是 0退化处理即可。3.3 组合权重乘法合成和线性加权怎么选只想要一份可解释的权重时AHP 就够想要权重反映候选方案的实际分布时加熵权法做校正。组合方式有两种常见写法线性加权w α·w_ahp (1-α)·w_entropy和乘法合成w (w_ahp · w_entropy) / Σ(w_ahp · w_entropy)。线性加权里 α 又是一个需要拍板的参数实操中容易引入新的主观性乘法合成不需要额外参数但对某一项为 0 的情况非常敏感任何一侧给 0 都会把整个权重压没。我一般会在评审类项目里用乘法合成因为它保留了“业务上不重要”这个硬约束——AHP 给了低权重熵权法再高也拉不回来而在纯数据驱动的方案排序里用线性加权α 取 0.5 作为起点再根据结果稳健性微调。无论哪种方式组合后都要重新归一化并且在报告里把两套原始权重都列出来方便别人判断结论是数据推出来的还是人定出来的。4. 合成算子怎么选M(·,) 与 M(∧,∨) 差的不只是一个符号R 和 A 都有了最后一步是把它们合成一个隶属向量 B A ∘ R。这里的“∘”不是普通矩阵乘法而是一族合成算子不同算子对应不同的决策语义选错会让结论的偏向性完全变掉。4.1 四种常用合成算子对照算子表达式决策语义典型场景M(∧,∨) 主因素决定b_j max_i min(a_i, r_ij)只要有一项特别突出就定调否决制评审、单指标一票通过M(·,∨) 主因素突出b_j max_i (a_i · r_ij)突出主要指标弱化次要指标关键指标筛选M(∧,⊕) 加权平均型变体b_j Σ min(a_i, r_ij)兼顾主次但有截断指标间存在明显短板约束M(·,) 加权平均b_j Σ a_i · r_ij所有指标按权重线性贡献综合评审、方案排序M(·,)是最常用的因为它满足“权重越大、贡献越大”的线性直觉而且结果天然归一A 和 R 每行都归一乘积的和也为 1。但它的缺点也很直接一个指标再差只要权重不够大就会被其他指标的平均值掩盖这恰恰是第 1 章里说的那个问题。所以选算子之前要先问一句这次评判是想要“综合印象”还是想要“短板拦截”前者用M(·,)后者用M(∧,∨)或带截断的算子。4.2 主因素决定型为什么会吃掉中间信息拿一组具体数字看差异比看公式直观得多。设权重 A [0.5, 0.3, 0.2]R 为三指标四等级A np.array([0.5, 0.3, 0.2]) R np.array([ [1.0, 0.0, 0.0, 0.0], [0.2, 0.6, 0.2, 0.0], [0.0, 0.3, 0.5, 0.2], ]) def compose(A, R, modeweighted): A np.asarray(A, dtypefloat) R np.asarray(R, dtypefloat) if mode weighted: # M(·,)加权平均 B A R elif mode minmax: # M(∧,∨)主因素决定 B np.array([np.max(np.minimum(A, R[:, j])) for j in range(R.shape[1])]) elif mode product_max: # M(·,∨)主因素突出 B np.array([np.max(A * R[:, j]) for j in range(R.shape[1])]) else: raise ValueError(mode) return B / B.sum() print(M(·,):, np.round(compose(A, R, weighted), 3)) print(M(∧,∨):, np.round(compose(A, R, minmax), 3))M(·,)的结果是[0.56, 0.24, 0.16, 0.04]最大项落在“优”且第二项“良”有 0.24 的份额能看出“整体不错但并非压倒性”。M(∧,∨)的结果是[0.417, 0.25, 0.167, 0.167]看上去也是“优”最大但第三、第四等级被拉平到同一个值原本 R 里“中”比“差”高出的那部分信息被 max 操作抹掉了。这就是主因素决定型的本质它只关心每个等级上最强的那个指标谁在某一等级上最突出谁就决定该等级的隶属度其他指标的细微差异不进入结果。评审里如果出现“一票否决”的需求它是合适的如果只是想排序它会系统性地放大极端项。# 短板拦截先把低于阈值的指标挑出来再决定是否继续合成 def gate(A, R, threshold0.3, level3): level: 关注第几个评语等级这里取“差”低于阈值直接返回警告 weak [(i, round(float(R[i, level]), 3)) for i in range(R.shape[0]) if R[i, level] threshold] if weak: return {gate: blocked, weak: weak} return {gate: pass, B: compose(A, R, weighted)}这段gate不是标准算法是我在实际项目里补的一道前置检查在跑加权平均之前先看有没有指标落在“差”这一档的隶属度超过阈值有就单独列出来人工复核而不是让它在平均里被冲淡。阈值取多少要看业务容忍度常见做法是 0.30.4取太高会频繁触发取太低又拦不住问题需要根据历史评审记录回测一遍再定。4.3 多级模糊评判把指标树拆成两级再合成指标超过 8 个、且天然分成几组的时候单级合成会让权重被稀释。这时候用两级评判先在每个二级维度内部做一次合成得到该维度的隶属向量再把这些向量当作新的 R用一级权重做第二次合成。def multi_level(tree, R_dict, modeweighted): tree: {一级指标: {weight: 一级权重, subs: {二级指标: 二级权重}}} R_dict: {二级指标: 该指标的隶属度行向量} level1_vec, level1_keys [], [] for top, cfg in tree.items(): subs cfg[subs] A np.array(list(subs.values()), dtypefloat) A A / A.sum() R np.vstack([R_dict[s] for s in subs]) level1_vec.append(compose(A, R, mode)) level1_keys.append(top) A_top np.array([tree[t][weight] for t in level1_keys], dtypefloat) A_top A_top / A_top.sum() R_top np.vstack(level1_vec) return compose(A_top, R_top, mode), dict(zip(level1_keys, level1_vec)) tree { 技术维度: {weight: 0.55, subs: {功能覆盖度: 0.4, 性能余量: 0.3, 稳定性: 0.3}}, 商务维度: {weight: 0.45, subs: {报价: 0.6, 交付周期: 0.4}}, } R_dict { 功能覆盖度: [0.7, 0.2, 0.1, 0.0], 性能余量: [0.3, 0.5, 0.2, 0.0], 稳定性: [0.1, 0.3, 0.4, 0.2], 报价: [0.4, 0.4, 0.2, 0.0], 交付周期: [0.2, 0.5, 0.3, 0.0], } B, detail multi_level(tree, R_dict) print(一级结果:, np.round(B, 3)) print(各维度:, {k: np.round(v, 3).tolist() for k, v in detail.items()})逻辑上multi_level先按tree里的subs顺序取出对应行拼成子矩阵套用同一套合成算子得到各维度的隶属向量再把它们纵向堆叠、用一级权重合成。参数说明tree的weight和subs里的权重各自独立归一化不需要事先调成 1函数里会重新归一化R_dict的键必须和subs完全一致拼错一个键np.vstack会直接报错mode在两级之间可以不一致比如二级用加权平均、一级用主因素突出但这种情况要在报告里写清楚否则复现的人对不上。提示两级评判的结果和单级评判有时会有明显差异这不是 bug而是权重分配结构变了。做对比实验时把两套结果的 R 都留档方便解释差异来源。5. 避坑与排查五个让评判结果不可信的具体问题模糊综合评判的代码量不大出问题基本都出在数据准备和参数设定上。下面五条是我在不同项目里反复碰到的翻车记录每条按“现象 → 原因 → 解决”写照着排查能省不少时间。5.1 某一行隶属度全为零现象R 矩阵打印出来某个指标整行都是 0.000合成后该指标对结果完全没有贡献。原因通常有两种一是该指标的原始打分全部落在所有三角形函数的定义域之外比如锚点 95/80/65/45专家打分集中在 30 分附近所有隶属函数都返回 0二是指标的取值方向和锚点方向相反负向指标没做同向化。解决方式是先把这一行对应的原始打分单独打出来看分布是否偏离锚点覆盖范围偏离了要么调整锚点、要么检查方向标签代码里保留全零行退化到均匀分布的分支只是兜底不能当作正常路径。5.2 CR 超过 0.1 还是把权重用了现象AHP 算出来的权重看着挺合理但评审结论和专家直觉对不上换个专家重填矩阵结论就大变。原因是一致性检验没过判断矩阵内部存在循环矛盾特征向量在这种矩阵上不稳定。解决方式不是调 RI 表而是定位最大矛盾三元组对每个 i、j、k 检查 a_ij · a_jk 和 a_ik 的偏离偏离最大的那一组请专家重新比较重填后重新算 CR直到连续两次低于阈值。如果多个专家各自填矩阵建议先做几何平均再算而不是先算权重再平均。5.3 负向指标没做同向化现象报价越低、缺陷率越低的方案合成结果反而越差。原因是负向指标直接用原值代入隶属函数“报价高”被判成“优”。解决办法是在构造 R 之前统一处理对负向指标做max - x或1/x变换对区间型指标用距离锚点的偏离度换算所有变换记录在代码注释里。这一步经常被跳过因为评语集和隶属函数写得像模像样跑起来不报错只有对着业务常识看结果才会发现不对。5.4 合成后 B 的各分量之和不等于 1现象合成得到的 B 四项加起来是 1.2 或 0.8。原因是选用了M(∧,∨)之类的算子这类算子本身不保证归一性尤其是当 A 和 R 没有严格按行归一的时候。解决方式是在合成之后统一做一次归一化B B / B.sum()同时检查 R 的每一行是否真的归一到 1——如果 R 有某行和明显偏离先修 R 再合成不要靠最后一步归一化掩盖问题。5.5 最大隶属度原则在结果接近均匀时失效现象B [0.28, 0.27, 0.25, 0.20]按最大隶属度原则取“优”但四个数几乎一样结论没有说服力。原因是评语等级之间区分度不足或者指标本身在专家组内部分歧很大。解决办法是同时输出置信度最大项与次大项的差差值低于 0.05 时不要直接下结论改为报告分布并提示“评价集中在优、良之间”必要时回到第 2 章检查锚点和 half把等级区分度拉开再重算。6. 结果怎么读最大隶属度原则失效时的一个替代做法拿到 B 之后直接取 argmax 是最省事的读法但它丢掉了分布信息也掩盖了“几个等级势均力敌”这种真实情况。我一般会同时输出三个量最大隶属度对应的等级、该等级的置信度最大项减次大项、以及用评语锚点算出的加权得分。前两个回答“像哪一档、有多确定”后一个回答“如果非要给一个分数是多少”三者一起看比单看 argmax 稳得多。def read_result(B, anchorsANCHORS, labels(优, 良, 中, 差)): B np.asarray(B, dtypefloat) B B / B.sum() order np.argsort(B)[::-1] top, second int(order[0]), int(order[1]) return { 等级: labels[top], 置信度: round(float(B[top] - B[second]), 3), 加权得分: round(float(B anchors), 2), 分布: dict(zip(labels, np.round(B, 3).tolist())), } print(read_result([0.56, 0.24, 0.16, 0.04])) print(read_result([0.28, 0.27, 0.25, 0.20]))第一组输出等级“优”、置信度 0.32、加权得分约 87.4结论可以放心用第二组等级同样是“优”但置信度只有 0.01加权得分约 76.1这时候报告里就该写“整体落在良与优之间优的倾向不明显”而不是简单写一个“优”。置信度的分界线没有统一标准我在评审场景里用 0.05 作为提示阈值、0.15 作为可下结论阈值低于前者只报分布介于两者之间报等级加保留意见。验证这套流程是否跑通最省事的办法是拿一个能手工核对的小例子走一遍两个指标、两个等级、权重各 0.5R 取[[0.8, 0.2], [0.3, 0.7]]加权平均结果是[0.55, 0.45]和手算对得上就说明合成链路没接错。之后再把真实数据灌进去出错时才有对照物。血泪经验是不要等最后出报告才发现 R 有问题。我现在的习惯是每跑完一次先把 R 逐行打印、把每行的和、最大值、最小值扫一遍再跑合成R 没问题后面的权重和算子才有讨论的意义。从那以后我每次接新的评审数据都强制先走一遍“打印 R → 肉眼扫行 → 小例子核对 → 再上真实数据”这四步慢是慢一点但省掉了大量回头解释结论为什么反常的时间。希望帮到你。本文还有配套的精品资源点击获取