You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

数组最大值查找函数的部分正确性证明:循环不变量维护解析

搞懂find_max函数的循环不变量与部分正确性证明

先唠清楚这个Eiffel函数是干啥的:它从非空数组里揪出最大值,核心逻辑靠循环实现,教授说的“部分正确性”,其实就是要证明两件事——循环结束时结果一定是对的,而且循环不会无限运行(咱们今天重点拆解不变量维护的部分)。

先搞明白循环不变量到底说啥

函数里的不变量有两种写法,本质是同一个意思:

  • 数学表达式:∀j | a.lower ≤ j < i • Result ≥ a[j]
  • Eiffel代码版:across a.lower |..| (i − 1) as j all Result >= a [j.item]

翻译成大白话就是:不管循环走到哪一步,在检查终止条件之前,当前的Result一定是数组从第一个元素(a.lower)到第i-1个元素这个范围内的最大值。简单说就是:我们已经扫过的元素里,没有比Result更大的。

一步步证明不变量是怎么被维护的

要证明部分正确性,得走三个关键环节:初始化时不变量成立,每次循环都能保住这个不变量,循环结束时不变量能导出正确结果。咱们挨个说:

1. 刚开始的时候,不变量成立吗?

循环启动前的from块是这么写的:

i := a.lower
Result := a [i]

这时候i等于数组的第一个元素下标,那i-1就比第一个下标还小,对应的范围a.lower |..| (i-1)是个空区间——连一个元素都没有。对于空集合来说,“所有元素都满足Result≥a[j]”这句话是天然成立的(毕竟没元素要验证),所以不变量一开始就站稳了脚跟。而且这时候Result就是第一个元素,也符合“已检查的元素(就它自己)的最大值就是它”的逻辑,没毛病。

2. 每次循环跑一圈,不变量还能保住吗?

假设在某次循环开始前,不变量是成立的——也就是Result已经是a.lower到i-1的最大值。接下来执行循环体(标准的find_max循环体应该是:i := i + 1; if a[i] > Result then Result := a[i] end)。

咱们分两种情况看:

  • 如果a[i] ≤ Result:这时候不用更新Result,它还是原来的数值。原来的Result已经是前i-1个元素的最大值,现在加上a[i],它也没超过Result,所以Result依然是前i个元素的最大值,不变量继续成立。
  • 如果a[i] > Result:这时候把Result换成a[i]。原来的Result是前i-1个元素的最大值,现在a[i]比它还大,那新的Result自然就是前i个元素的最大值,不变量照样成立。

不管哪种情况,跑完循环体后,不变量都能稳稳保持,这就证明了每次迭代都能维护住这个“承诺”。

3. 循环结束时,不变量能帮我们得到正确结果吗?

循环的终止条件是i > a.upper。当循环停下来的时候,不变量依然成立:Result是a.lower到i-1的最大值。这时候i已经超过了数组的最后一个下标,那i-1刚好就是数组的最后一个下标a.upper——也就是说,Result是整个数组所有元素的最大值,完全符合函数要返回的结果!这就把“部分正确性”给坐实了。

最后总结下

循环不变量就像我们给自己定的一个“规矩”:每次循环前,都确保已经检查过的元素里,Result是最大的。从一开始的初始状态,这个规矩一直守得住,直到循环结束时,我们已经检查完了所有元素,这个规矩就变成了“Result是整个数组的最大值”,完美达成目标。

内容的提问来源于stack exchange,提问作者Noor Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:25:02