从Richardson到MINBERR:线性迭代求解器的后向误差收敛性分析

📅 2026/6/22 22:04:21
从Richardson到MINBERR:线性迭代求解器的后向误差收敛性分析
1. 项目概述为什么我们要关心后向误差在数值计算的世界里解一个线性方程组Ax b是再基础不过的任务。无论是模拟流体力学、训练机器学习模型还是优化电路设计最终往往都绕不开这一步。我们通常的注意力都放在“前向误差”上也就是我们算出来的解x_approx和真实解x_true之间的差距||x_true - x_approx||。这个差距当然重要它直接衡量了我们解的精度。但这里有个更微妙、也常常被忽视的视角后向误差。它问的不是“我的解离标准答案有多远”而是“我的解对于‘另一个’稍微不同的原始问题来说是不是一个完美的精确解” 换句话说是否存在一个微小的扰动ΔA和Δb使得(A ΔA) * x_approx b Δb严格成立如果存在并且||ΔA||和||Δb||都非常小那么即使前向误差可能因为问题的“病态”而显得很大我们的解x_approx在“后向”意义上依然是高质量的——它完美地解决了一个与原始问题极其相近的问题。理解后向误差对于评判一个迭代求解器比如 Richardson 迭代、MINRES、GMRES 等的性能至关重要。很多时候算法迭代停止时残差r b - A*x已经很小了但解可能因为矩阵条件数太大而并不精确。后向误差分析能告诉我们这个解在多大程度上是“向后稳定”的这比单纯看残差范数更能反映算法在数值上的鲁棒性。今天我们就从一个经典的简单算法——Richardson 迭代——入手一直讨论到针对非对称矩阵的 MINBERR 算法来梳理一套通用的、用于分析迭代求解器后向误差收敛性的框架。你会发现这套思路不仅能帮你更深刻地理解算法行为还能在实际调试中给你提供更可靠的停止准则。2. 核心概念前向误差、后向误差与算法收敛性在深入具体算法之前我们必须把几个核心概念及其相互关系掰扯清楚。这是理解后续所有分析的基础。2.1 前向误差最直观的精度标尺前向误差的定义非常直接。假设我们要求解Ax b其中A是n×n非奇异矩阵。设真实解为x*我们的迭代算法在第k步产生的近似解为x_k。那么前向误差就是前向误差 ||x* - x_k||这里||.||通常指代向量的 2-范数欧几里得范数。这个误差衡量的是近似解与理想目标之间的绝对距离。然而在绝大多数实际应用中x*是未知的否则我们就不需要求解了。因此我们无法直接计算前向误差。我们只能通过一些可计算的量来间接估计或约束它这就引出了残差和后向误差。2.2 残差算法直接控制的量残差是迭代算法中天然产生且可精确计算的量r_k b - A * x_k残差范数||r_k||的大小直观反映了当前近似解x_k在多大程度上“满足”了原方程。对于许多迭代法如最速下降法、共轭梯度法算法的设计目标就是最小化残差范数。但这里有一个关键的陷阱小的残差并不一定意味着小的前向误差。两者之间的关系由矩阵A的条件数κ(A) ||A|| * ||A^{-1}||决定||x* - x_k|| / ||x*|| ≤ κ(A) * (||r_k|| / ||b||)这个不等式告诉我们即使残差相对范数||r_k||/||b||已经小到1e-10如果矩阵的条件数κ(A)高达1e12那么前向相对误差仍可能大到1e-2。这就是所谓的“病态”问题方程对输入数据的微小扰动极其敏感。2.3 后向误差一个更稳定的评价标准后向误差跳出了“与未知真解比较”的框架转而审视近似解对于原始问题的“解释力”。它的定义是寻找尽可能小的矩阵扰动E_k和向量扰动f_k使得当前近似解x_k成为如下扰动系统的精确解(A E_k) * x_k b f_k那么后向误差η(x_k)就可以定义为满足上述等式的扰动对(E_k, f_k)的某种范数组合的最小值。一种常见且易于计算的范数是分量式component-wise或范数式norm-wise的后向误差。例如范数式后向误差的一个常用定义为η(x_k) min { ε : 存在 ΔA 和 Δb 使得 (A ΔA) x_k b Δb, 且 ||ΔA|| ≤ ε ||A||, ||Δb|| ≤ ε ||b|| }可以证明对于这个定义后向误差有一个非常简洁的计算公式η(x_k) ||r_k|| / (||A|| * ||x_k|| ||b||)这个公式太有用了它意味着我们不需要知道真解x*也不需要去解一个优化问题仅凭当前已知的A,b,x_k和计算出的残差r_k就能立刻算出一个可靠的后向误差估计。后向误差的直观意义如果η(x_k)很小比如达到机器精度级别那就说明x_k是某个与原始问题在数值上几乎无法区分的“邻近问题”的精确解。从工程应用角度看如果原始数据A和b本身就来自测量或建模带有固有误差那么一个后向误差很小的解其实用价值可能比一个前向误差稍小但后向误差很大的解更高因为它与数据的匹配程度在可接受的误差范围内是完美的。2.4 收敛性我们到底在收敛什么当我们说一个迭代法“收敛”时必须明确收敛的对象。残差收敛||r_k|| → 0。这是最常被监控的。前向误差收敛||x* - x_k|| → 0。这是我们最终想要的但无法直接观测。后向误差收敛η(x_k) → 0。这是一个可观测的、且数值上更稳定的收敛指标。一个理想的迭代求解器应该能同时保证这三者的收敛。但实际情况中由于数值舍入误差和问题病态性它们的行为可能不同。后向误差收敛性分析就是研究在不同算法框架下η(x_k)随着迭代步数k增加而衰减的规律。这能揭示算法内在的数值稳定性。3. 从经典出发Richardson迭代的后向误差分析Richardson迭代虽然简单甚至很少直接用于解决实际问题但它像一块完美的“试金石”能清晰地展示后向误差分析的基本原理而且其结论可以推广到更复杂的算法。3.1 Richardson迭代算法回顾标准一阶定常Richardson迭代的格式是x_{k1} x_k ω * (b - A * x_k)其中ω是一个固定的松弛参数。将其重写可以得到残差的递推关系r_{k1} b - A * x_{k1} (I - ωA) * r_k这里I是单位矩阵。如果令G I - ωA那么r_k G^k * r_0。算法的收敛速度完全由迭代矩阵G的谱半径ρ(G)决定。当ρ(G) 1时残差范数会以渐近速率ρ(G)线性收敛到零。3.2 后向误差的递推与收敛性对于 Richardson 迭代我们可以直接推导其后向误差η_k ||r_k|| / (||A|| * ||x_k|| ||b||)的动态变化。这个推导过程很有启发性。首先我们注意到迭代格式可以改写为A * x_k r_k b这正是残差的定义。现在考虑第k步的后向误差η_k。根据定义η_k正比于||r_k||。而||x_k||在迭代过程中通常会趋近于||x*||。因此分母(||A|| * ||x_k|| ||b||)在迭代后期会趋于一个常数C。那么后向误差的递推近似为η_{k1} ≈ (||r_{k1}|| / C) (||G * r_k|| / C) ≤ (||G|| * ||r_k|| / C) ||G|| * η_k这个不等式表明后向误差η_k的上界也以||G||的速率线性收敛。这里||G||是矩阵G的算子范数通常取2-范数。由于ρ(G) ≤ ||G||所以后向误差的收敛速度可能比残差本身的渐近速度由ρ(G)控制要慢一些但它依然是线性收敛的。实操心得这个简单的分析告诉我们一个关键点对于 Richardson 这类定常迭代只要算法是残差收敛的ρ(G) 1那么它的后向误差也必然是收敛的。监控残差范数||r_k||本质上就在监控后向误差的上界。在实际编写 Richardson 迭代代码时除了计算残差完全可以同时计算并输出η_k它能给你一个关于解的质量的、对病态性相对不敏感的评价。3.3 数值实验与观察让我们用一个简单的 MATLAB 实验来验证一下。我们生成一个对称正定矩阵A和一个随机向量b使用最优参数ω 2 / (λ_max λ_min)进行 Richardson 迭代。% 生成一个条件数较大的对称正定矩阵 n 100; A gallery(poisson, 10); % 产生一个100x100的泊松矩阵条件数约为O(1e3) b randn(n, 1); x_true A \ b; % 使用反斜杠计算“真解”作为参考 % Richardson迭代参数计算 lambda eig(A); omega_opt 2 / (max(lambda) min(lambda)); G eye(n) - omega_opt * A; spectral_radius max(abs(eig(G))); % 初始化 x zeros(n, 1); r b - A * x; max_iter 1000; tol 1e-12; % 存储收敛历史 res_norm_history zeros(max_iter, 1); fwd_err_history zeros(max_iter, 1); bwd_err_history zeros(max_iter, 1); norm_A norm(A, 2); norm_b norm(b, 2); for k 1:max_iter res_norm norm(r); res_norm_history(k) res_norm; fwd_err_history(k) norm(x_true - x); bwd_err_history(k) res_norm / (norm_A * norm(x) norm_b); % 后向误差计算 if res_norm / norm_b tol break; end x x omega_opt * r; r b - A * x; % 更新残差 end % 绘制收敛曲线 figure; semilogy(1:k, res_norm_history(1:k)/norm_b, b-, LineWidth, 1.5, DisplayName, 相对残差 ||r_k||/||b||); hold on; semilogy(1:k, fwd_err_history(1:k)/norm(x_true), r--, LineWidth, 1.5, DisplayName, 相对前向误差); semilogy(1:k, bwd_err_history(1:k), g-., LineWidth, 1.5, DisplayName, 后向误差 η(x_k)); xlabel(迭代步数 k); ylabel(误差); legend(show); grid on; title(Richardson迭代三种误差的收敛历史);运行这段代码你会典型地看到相对残差和后向误差的曲线几乎重叠并且以相似的线性速率下降。而相对前向误差的曲线在初期与它们平行下降但下降到一定程度由机器精度和条件数决定后便停滞不前形成了一个“平台期”。这个平台的高度大约为κ(A) * ε_machine其中ε_machine是机器精度。这个实验直观地展示了对于病态问题残差和后向误差可以继续减小但前向误差受限于条件数无法进一步提高。因此将后向误差作为迭代停止准则比单纯追求残差小到机器精度更有实际意义可以避免不必要的迭代。4. Krylov子空间方法的后向误差通用框架Richardson迭代是定常迭代的代表。而对于更高效、更常用的Krylov子空间方法如CG, MINRES, GMRES, BICGSTAB等其后向误差分析需要在一个更统一的框架下进行。这类方法的共同点是在第k步迭代近似解x_k取自某个Krylov子空间K_k(A, r_0)并且通常强制某种残差正交性或最小化条件。4.1 Krylov方法解的一般形式与后向误差设初始残差r_0 b - A*x_0。经过k步Krylov迭代后我们得到近似解x_k x_0 V_k * y_k。其中V_k的列向量张成了Krylov子空间K_k(A, r_0)y_k是一个k维向量由算法特定的条件如Ritz值最小化、残差正交决定。此时残差可以写为r_k b - A * x_k r_0 - A * V_k * y_k对于许多Krylov方法如Arnoldi过程生成的GMRES或MINRES存在一个(k1)×k的上Hessenberg矩阵H_k满足A * V_k V_{k1} * H_k。同时r_0可以表示为r_0 ||r_0|| * V_{k1} * e_1其中e_1是第一个单位向量。代入残差公式r_k V_{k1} * (||r_0|| * e_1 - H_k * y_k)由于V_{k1}的列是标准正交的所以残差范数||r_k|| || ||r_0|| * e_1 - H_k * y_k ||。在GMRES和MINRES中y_k正是通过最小化这个范数来选取的。现在来看后向误差η(x_k) ||r_k|| / (||A|| * ||x_k|| ||b||)。分子||r_k||是算法显式最小化的量。分母中的||x_k||在迭代过程中通常会增长并最终趋于稳定。关键在于对于Krylov方法后向误差的收敛性直接与算法最小化残差范数的能力挂钩。因为||A||和||b||是常数所以η(x_k)的下降速度至少和||r_k||一样快。4.2 一个关键性质后向误差的单调性对于像MINRES和GMRES这样的长递推方法使用完全正交化有一个非常好的性质它们产生的残差范数||r_k||是单调不增的。这意味着随着迭代进行残差只会变小或保持不变不会反弹。由于后向误差η(x_k)正比于||r_k||分母变化相对缓慢因此在这些方法中后向误差在迭代过程中也基本表现出单调下降的趋势。这为使用后向误差作为可靠的停止准则提供了坚实的理论基础。相比之下像BiCGSTAB或QMR这样的短递推方法残差范数可能震荡其后向误差也可能随之波动。在这种情况下监控后向误差就显得更为重要因为它能帮助你判断当前解是否真的在“向后稳定”的意义上有所改进而不是被震荡的残差所误导。注意事项当||x_k||非常小例如解向量本身接近零向量时后向误差公式η(x_k) ||r_k|| / (||A|| * ||x_k|| ||b||)的分母可能由||b||主导。此时η(x_k)的行为与相对残差||r_k||/||b||几乎一致。但在大多数情况下尤其是迭代后期x_k趋近于真解时||A|| * ||x_k||项与||b||量级相当后向误差能提供比相对残差更均衡的评价。5. 聚焦非对称问题MINBERR算法的收敛性剖析现在我们把目光投向标题中提到的MINBERR算法。MINBERR是“Minimal Backward Error using Ritz Residuals”的缩写它是一种专门为求解非对称线性系统设计的迭代方法其核心思想是直接最小化后向误差的某个上界而不是最小化残差范数。5.1 MINBERR算法的动机与原理对于非对称矩阵A标准的Krylov方法如GMRES是通过最小化残差2-范数||b - A*x_k||来选取x_k的。然而正如我们前面分析的小的残差并不保证小的后向误差尤其是在矩阵非正规或病态严重时。MINBERR算法的设计者意识到后向误差是一个更稳健的目标函数。但是直接最小化η(x_k) ||r_k|| / (||A|| * ||x_k|| ||b||)是一个非线性问题计算复杂。MINBERR采用了一个巧妙的近似策略。它注意到在Krylov子空间x_k ∈ x_0 K_k(A, r_0)中后向误差可以关联到所谓的“Ritz残差”。在第k步Arnoldi过程产生关系A * V_k V_{k1} * H_k。一个Ritz对(θ, s)满足H_k * s ≈ θ * s其中H_k是H_k的k×k部分去掉最后一行。对应的Ritz近似特征向量为u V_k * s其残差为A*u - θ*u V_{k1} * (H_k * s - θ * [s; 0])。这个残差范数是可计算的。MINBERR算法的核心是在每一步它从当前的Krylov子空间中选择一个近似解x_k使得与之关联的某个Ritz残差范数最小化。这个Ritz残差范数被证明是后向误差η(x_k)的一个上界。因此最小化这个Ritz残差就是在间接地最小化后向误差的上界。5.2 算法步骤与实现要点MINBERR算法的迭代步骤可以概括如下它通常与Arnoldi过程交织进行Arnoldi扩展执行一步Arnoldi过程扩展Krylov子空间基V_k和上Hessenberg矩阵H_k。Ritz值提取计算当前H_k或其某个子矩阵的Ritz值θ_i和Ritz向量s_i。后向误差上界计算对于每个Ritz对(θ_i, s_i)计算其对应的后向误差上界估计量。这通常涉及计算一个形如|| (H_k - θ_i I) * s_i || / (σ_max(H_k) * ||s_i|| ...)的量其中σ_max(H_k)近似||A||。选择与更新选取使得后向误差上界最小的那个Ritz对对应的系数来更新当前近似解x_k。检查收敛计算当前解x_k的真实后向误差η(x_k)使用原始A和b。如果小于容忍度则停止否则返回步骤1。与GMRES相比MINBERR在每一步需要额外的Ritz值计算和后向误差上界评估计算开销稍大。但它换来的潜在好处是对于某些导致GMRES残差停滞即收敛缓慢的非对称问题MINBERR可能通过直接瞄准后向误差而获得更稳定的收敛行为。5.3 MINBERR收敛性分析的关键点分析MINBERR的收敛性本质上是分析其最小化的目标函数——后向误差上界——的下降行为。由于这个上界基于Ritz残差其收敛性与Arnoldi过程对矩阵A的特征值/特征向量的逼近速度紧密相关。与多项式逼近的关系像GMRES的残差可以表示为r_k φ_k(A) * r_0其中φ_k是一个最小化多项式。MINBERR的后向误差上界也与一个在Ritz值上取值的多项式有关。其收敛速度取决于能否在复平面上找到一个多项式该多项式在A的谱值上较小同时该多项式在原点处的值对应解向量的范数不会过大。这比单纯最小化残差多项式更复杂。优势场景理论上MINBERR在矩阵的伪谱pseudospectrum分布较为不利时例如非正规矩阵其特征值对扰动极其敏感可能比GMRES表现更优。因为后向误差与伪谱的联系比残差更直接。MINBERR直接控制后向误差相当于在求解一个对扰动不敏感的量。数值稳定性由于MINBERR的决策基于H_k的Ritz值当H_k病态时对于高度非正规矩阵的Arnoldi过程可能出现Ritz值的计算可能不准确从而影响算法选择。这是实现时需要特别注意的通常需要引入稳健的Ritz值提取技术并可能需要对H_k进行隐式重启以控制条件数。实操心得与常见陷阱实现复杂度自己实现一个稳健的MINBERR算法并不简单。除了标准的Arnoldi过程你还需要一个可靠的稠密特征值求解器如QR算法来从H_k中提取Ritz值。对于大规模问题这可能会成为瓶颈。通常建议使用成熟的数值线性代数库如SLEPc、BLOPEX或MATLAB的eigs函数在子空间上操作中的工具。开销与收益权衡MINBERR每步迭代的计算量高于GMRES。在实际问题中应先尝试标准的GMRES。只有当GMRES表现出异常的停滞且问题被怀疑是高度非正规时才考虑尝试MINBERR这类以最小化后向误差为目标的算法。停止准则对于MINBERR最自然的停止准则就是计算真实的后向误差η(x_k)。由于算法本身就在优化它的上界所以当η(x_k)达到预设容忍度时停止是非常合理的。这避免了像GMRES那样可能残差很小但后向误差仍较大的情况。重启策略像GMRES一样MINBERR也可能需要重启以防止子空间维度过大。重启时需要保留当前最佳近似解x_k作为新的初始向量。重启后的收敛性可能会受到影响这是所有重启型Krylov方法的共性问题。6. 通用收敛性分析框架与实用建议通过对从Richardson到MINBERR的分析我们可以提炼出一个分析迭代求解器后向误差收敛性的通用框架。这个框架不仅有助于理解算法也能指导实践。6.1 建立分析框架的四个步骤确定迭代格式与解空间首先明确算法如何生成近似解序列{x_k}。它是定常迭代如x_{k1} G*x_k f还是Krylov子空间方法x_k ∈ x_0 K_k(A, r_0)解空间的性质决定了分析的工具。表达后向误差将算法特定的后向误差η(x_k)用算法已知量表示出来。对于大多数基于残差的方法通用公式η(x_k) ||r_k|| / (||A|| * ||x_k|| ||b||)是一个很好的起点。对于像MINBERR这样直接优化不同目标的方法则需要使用其特定的目标函数表达式。寻找控制量分析是什么控制着η(x_k)的衰减。对于Richardson是迭代矩阵的范数||G||对于GMRES是最小化后的残差范数||r_k||对于MINBERR是Ritz残差的最小值。建立η(x_k)与这个控制量之间的不等式关系。分析控制量的收敛性最后分析这个控制量随着迭代k增加的衰减速率。这通常涉及到矩阵谱分析、多项式逼近理论或Ritz值逼近理论。例如GMRES的残差收敛速率可以用特征值分布或场值field of values来刻画MINBERR的后向误差上界收敛速率则与Ritz值对矩阵伪谱的逼近有关。6.2 给实践者的核心建议基于上述分析在工程实践中应用迭代求解器时你可以遵循以下建议来获得更可靠的结果监控后向误差而不仅仅是残差在你的求解器代码中增加后向误差η(x_k)的计算和输出。它的计算成本很低一次矩阵范数估计和每次迭代的向量范数计算但提供的信息价值很高。设置停止准则时可以同时考虑相对残差和后向误差例如max(||r_k||/||b||, η(x_k)) tol。理解问题的“健康”状况在求解前或求解初期估计一下矩阵A的条件数κ(A)。如果条件数很大比如 1e10那么就要做好心理准备前向误差可能无法达到机器精度。此时后向误差是一个更合理的精度目标。你可以通过condest(A)MATLAB或类似函数来快速估计。算法选择策略对于对称正定问题首选共轭梯度法CG。它不仅残差单调下降其误差范数A-范数也有明确的优化意义。对于对称不定问题MINRES是标准选择它能保证残差2-范数单调下降。对于一般的非对称问题GMRES是首选。如果遇到收敛缓慢考虑使用预处理技术这通常是比换算法更有效的加速手段。仅当问题高度非正规且经过充分预处理后GMRES依然表现不佳时可以考虑像MINBERR这样以后向误差为目标的算法作为专家级备选方案。预处理是王道无论使用哪种迭代法一个合适的预处理器M使得M^{-1}A的谱分布更集中、条件数更小对收敛速度的提升是颠覆性的。好的预处理能将一个无法求解的问题变得可解。设计或选择预处理器的艺术往往是解决大规模稀疏线性系统的关键。利用MATLAB等工具进行诊断MATLAB的迭代求解函数如pcg,minres,gmres通常都提供输出迭代历史的功能。使用这些功能绘制残差和后向误差的收敛曲线。观察曲线是否平滑下降、是否有平台期或震荡这能帮助你诊断问题是病态的、非正规的还是预处理器效果不佳。6.3 一个综合的MATLAB诊断示例以下代码展示了如何在用gmres求解一个非对称问题时同时监控残差和后向误差并做出诊断。% 生成一个非对称、非正规的测试矩阵 n 200; A gallery(grcar, n, 3); % grcar矩阵是典型的非正规矩阵 A A 0.1 * sprandn(n, n, 0.05); % 添加一些随机性 A sparse(A); b randn(n, 1); % 使用GMRES求解并输出迭代历史 maxit 150; restart 50; % GMRES重启周期 tol 1e-10; % 预分配历史记录 resvec zeros(maxit, 1); % GMRES内置的残差历史记录的是预条件残差这里我们手动计算真残差 bwdvec zeros(maxit, 1); x0 zeros(n, 1); norm_A normest(A); % 估计矩阵2-范数比norm(full(A))快 norm_b norm(b); % 使用带函数句柄的输出手动计算真残差和后向误差 iter 0; function stop outputFcn(x, ~, ~, ~) iter iter 1; if iter maxit r_true b - A * x; resvec(iter) norm(r_true); bwdvec(iter) resvec(iter) / (norm_A * norm(x) norm_b); end stop false; end [x, flag, relres, iters] gmres(A, b, restart, tol, maxit/restart, [], [], x0, outputFcn); iters_total (iters(1)-1)*restart iters(2); % 计算总迭代步数 % 绘制收敛历史 figure; semilogy(1:iter, resvec(1:iter)/norm_b, b-o, MarkerIndices, 1:10:iter, DisplayName, 相对残差 (真)); hold on; semilogy(1:iter, bwdvec(1:iter), r-s, MarkerIndices, 5:10:iter, DisplayName, 后向误差 η(x_k)); xlabel(迭代步数); ylabel(误差度量); legend(show); grid on; title(sprintf(GMRES收敛历史 (Flag%d, 总迭代%d), flag, iters_total)); % 诊断 fprintf(最终相对残差: %.2e\n, resvec(iter)/norm_b); fprintf(最终后向误差: %.2e\n, bwdvec(iter)); fprintf(条件数估计: %.2e\n, condest(A)); if bwdvec(iter) 1e-12 fprintf(✅ 后向误差已达到很高精度解在数值上是向后稳定的。\n); elseif resvec(iter)/norm_b 1e-12 bwdvec(iter) 1e-8 fprintf(⚠️ 残差很小但后向误差较大。解可能对数据扰动敏感或矩阵条件数很大(估计为%.1e)。\n, condest(A)); else fprintf( 迭代可能尚未收敛或收敛缓慢。考虑使用预处理或尝试其他算法。\n); end运行这段代码你可以直观地看到两种误差度量的下降轨迹。如果后向误差曲线始终在残差曲线之上且下降缓慢这可能提示矩阵的非正规性在影响解的向后稳定性。这种诊断对于选择更合适的算法如切换到MINBERR风格的求解器或改进预处理器非常有帮助。从经典的Richardson迭代到现代的MINBERR算法后向误差为我们提供了一个统一且强大的视角来理解和评估线性迭代求解器的表现。它架起了算法理论数值稳定性与实际计算可靠性之间的桥梁。下次当你调试一个收敛缓慢的求解过程时不妨先看一眼后向误差的曲线它或许能告诉你比残差更多的故事。