循环不变式

引言

  算法程序形式化设计和证明是确保算法程序逻辑结构正确的最理想途径,是保证软件可靠性的有效手段之一;而体现了算法程序本质特征的循环不变式在
算法程序形式化方法中具有十分重要的作用。循环不变式是程序设计理论中的一个重要概念。这一概念的建立在程序设计从艺术走向科学这一历
史性的转变过程中起着巨大的推动作用,它不仅可以帮助人们理解那些难以理解的、精巧的循环算法程序,而且可以用来形式化地证明循环算法程序的正确性,也有
助于形式化地开发出高质量的循环程序 。

  然而循环不变式的开发一直是算法程序设计领域中最具挑战性、最富有创造性的问题之一,许多计算机科学家和计箅机工作者也致力于这方面的研究。目前,人们把循环不变式定义为在循环的每次执行前后都为真的谓词 。

本文地址:http://www.cnblogs.com/archimedes/p/loop-invariant.html,转载请注明源地址。

定义

循环不变式:反映循环体中所有循环变量的变化规律并在循环体执行前后都为真的谓词称为该循环体的循环不变式 。

循环变量:在循环体中,其值随着循环体的执行不断发生变化的变量称为循环变量 。

算法导论第二章中的原文是:We state these properties of A[1 ‥ j -1] formally as a
loop invariant。其中举的例子是插入排序,每次循环从数组A中取出第j个元素插入有序区A[1 .. j-1],然后递增j。这样A[1
.. j-1]的有序性始终得到保持,这就是所谓的“循环不变 (loop invariant)”了。

这个概念主要用来检验算法的正确性。原文如下:

We use loop invariants to help us understand why an algorithm is correct. We must show three things about a loop invariant:

Initialization: It is true prior to the first iteration of the loop.

Maintenance: If it is true before an iteration of the loop, it remains true before the next iteration.

Termination: When the loop terminates, the invariant gives us a useful property that helps show that the algorithm is correct.

1. 初始化(循环第一次迭代之前)的时候,A[1 ‥ j -1]的“有序性”是成立的;

2. 在循环的每次迭代过程中,A[1 ‥ j -1]的“有序性”仍然保持;

3. 循环结束的时候,A[1 ‥ j -1]的“有序性”仍然成立。

编写循环时,找到让每次循环都成立的逻辑表达式很重要。这种逻辑表达式称为循环不变式(loop invariant)。循环不变式相当于用数学归纳法证明的“断言”。

循环不变式用于证明程序的正确性。在编写循环时,思考一下“这个循环的循环不变式是什么”就能减少错误。

举例应用

以一个非常简单的例子来讲解循环不变式吧。

代码清单是用C 语言写的sum 函数,功能是求出数组元素之和。参数array[] 是要求和的对象数组,size 是这个数组的元素数。调用sum 函数,会获得array[0] 至array[size-1] 的size 个元素之和。

代码清单--sum函数,求出数组的元素之和

int sum(int array[], int size)
{
    int k = 0;
    int s = 0;
    while(k < size) {
        s = s + array[k];
        k = k + 1;
    }
    return s;
}

在sum 函数中使用了简单的while 循环语句。我们从数学归纳法的角度来看这个循环,得出下述断言M(n)。这个断言就是循环不变式。

• 断言M (n ) :数组array 的前n 个元素之和,等于变量s 的值。

我们在程序中成立的断言上标注注释,形成清单4-3 所示代码。

代码清单--在上面的代码中成立的断言上标注注释

在代码清单的第4 行,s 初始化为0。由此,第5 行的M(0) 成立。M(0) 即为“数组array 的前0 个元素之和等于变量s 的值”。这相当于数学归纳法的步骤1。

数学归纳法的步骤1(M(0)成立)

第7 行中,M(k) 成立。然后进行第8 行的处理, 将数组array[k] 的值加入s, 因此M(k+1) 成立。这相当于数学归纳法的步骤2。

数学归纳法的步骤2(M(k) M(k+1)成立)

请一定要理解第8 行,
s=s+array[k];

意为“在M(k) 成立的前提下,M(k+1) 成立”。

第10 行中k 递增1,所以第11 行的M(k) 成立。这里是为了下一步处理而设定变量k的值。

最后,第13 行的M(size) 成立。因为while 语句中的k 递增了1,而这时一直满足M(k), 走到第13 行时k 和size 的值相等。M(size) 成立说明sum 函数是没有问题的。因此,第14 行return 返回结果。

M (size )成立

综上所述,这个循环在k 从0 增加到size 的过程中一直保持循环不变式M(k)
成立。编写循环时,有两个注意点。一个是“达到目的”,还有一个是“适时结束循环”。循环不变式M(k) 就是为了确保“ 达到目的”。而k 从0
到size 递增确保了“适时结束循环”。

在下面的代码清单中,写明了M(k) 成立的同时k 递增的情形。(∧表示“并且”)

代码清单-- M (k )成立的同时k递增



 参考资料:

《算法导论》

《循环不变式开发新策略及其应用》

时间: 2024-09-08 17:25:49

循环不变式的相关文章

《算法基础:打开算法之门》一2.3 循环不变式

2.3 循环不变式 对于线性查找的3个算法,我们能很容易地看到每个算法均能生成正确的结果.但是有时候生成正确的结果看起来有点难.这涉及一系列技术,在这里不能一一讲解. 证明正确性的一个常用方法是使用循环不变式证明:即证明循环的每次迭代之前循环不变式为真.循环不变式能够帮助我们证明正确性,关于循环不变式,我们必须证明以下3条性质. 初始化:循环的第一次迭代之前,它为真. 保持:如果循环的每次迭代之前它为真,那么下次迭代之前它仍为真. 终止:循环终止时,当它确实终止时,伴随循环终止的原因,循环不变式

C++编程:C++归并排序实现(算法导论)

  算法导论上的下标是从1开始的,但是为了和c++ STL的设计思想一致,所有函数的实现统一用左闭右开区间.中间修改了很多次,因为下标修改不是很容易就改掉的,需要始终维持循环不变式,稍微一个步骤出错就会使结果有些错误. #include #include #include #include using namespace std; void merge(int *A, int p, int q, int r) { //merge [p, r),左闭右开区间 int n1 = q - p, n2

《从问题到程序:用Python学编程和计算》——导读

前 言 计算机诞生至今不过六七十年,但它已经改变了世界,改变了每个人的生活.人们每天都在与计算机交流(如智能手机),各领域专业人员的大量日常工作都需要使用计算机,从事与计算机相关工作的人们已经发展为社会上最大的专业技术社团.计算机的研究和应用.互联网和其他相关领域,还在不断呼唤大量熟悉计算机的专业开发人才.计算机科学技术的开发和应用能力已被广泛认为是国家竞争力的重要组成部分.因此,学习计算机科学技术知识,不仅是社会发展的需要,而且已成为个人的重要职业竞争力.然而,要深入理解计算和计算机,使其成为

《算法基础:打开算法之门》一3.5 快速排序

3.5 快速排序 像归并排序那样,快速排序也使用分治模式(因此也使用递归).然而,快速排序与归并排序所使用的分治法稍微有点不同.与归并排序相比,存在两个不同之处: ●快速排序按照原址工作. ●快速排序的渐近运行时间介于最坏情况和平均情况之间.尤其是,快速排序的最坏运行时间是Θ(n2),但是它的平均情况下的运行时间要更好一些:Θ(nlgn).快速排序也有好的常数因子(比归并排序更好一些),并且它通常是实践中的一个好的排序算法.这里是快速排序使用分治法的过程.再一次考虑对书架上的书进行排序的例子.就

《从问题到程序:用Python学编程和计算》——练习

练习 概念和理解 1.   复习下面概念:数值积分,区间分割法,舍入误差,简单重复,累积,累积变量,生成和筛选,递推,递推变量,素数(质数),因子和真因子,哥德巴赫猜想,输入循环,输入控制的循环,递归定义,递归函数,循环定义,无穷递归,循环和递归,斐波那契数列,二路递归,计时,循环不变式,计算复杂性,最大公约数,欧几里得算法(辗转相除法),河内塔问题,自递归,相互递归,程序终止性,不可判定,Collatz猜想,唯一定义原则,自下而上,自顶向下,逐步求精,形式参数(形参),文档串,实际参数(实参)

《从问题到程序:用Python学编程和计算》——3.5 练习

3.5 练习 概念和理解 1. 复习下面概念:数值积分,区间分割法,舍入误差,简单重复,累积,累积变量,生成和筛选,递推,递推变量,素数(质数),因子和真因子,哥德巴赫猜想,输入循环,输入控制的循环,递归定义,递归函数,循环定义,无穷递归,循环和递归,斐波那契数列,二路递归,计时,循环不变式,计算复杂性,最大公约数,欧几里得算法(辗转相除法),河内塔问题,自递归,相互递归,程序终止性,不可判定,Collatz猜想,唯一定义原则,自下而上,自顶向下,逐步求精,形式参数(形参),文档串,实际参数(实

《算法基础:打开算法之门》一导读

前言 Algorithms Unlocked 计算机是如何解决问题的呢?小小的GPS是如何只在几秒钟内就从无数条可能路径中找出到达目的地的最快捷路径的呢?在网上购物时,又如何防止他人窃取你的信用卡账号呢?解决这些问题,以及大量其他问题的答案均是算法.我写本书的目的就是为你打开算法之门,解开算法之谜. 我是<算法导论>的合著者之一.<算法导论>是一本特别好的书(当然,这是我个人的主观评价),但是它确实相当专业. 本书并不是<算法导论>,甚至不能被称为一本教材.它既没有对计

《算法导论(原书第3版)》一2.3 设计算法

2.3 设计算法 我们可以选择使用的算法设计技术有很多.插入排序使用了增量方法:在排序子数组A[1..j-1]后,将单个元素A[j]插入子数组的适当位置,产生排序好的子数组A[1..j]. 本节我们考查另一种称为"分治法"的设计方法.第4章将更深入地探究该方法.我们将用分治法来设计一个排序算法,该算法的最坏情况运行时间比插入排序要少得多.分治算法的优点之一是,通过使用第4章介绍的技术往往很容易确定其运行时间. 29 2.3.1 分治法 许多有用的算法在结构上是递归的:为了解决一个给定的

《算法基础:打开算法之门》一3.1 二分查找

3.1 二分查找 在学习一些排序算法之前,首先学习二分查找,其中待查找的数组事先需要是有序的.二分查找的优点是从包含n个元素的数组中执行查找操作仅仅需要O(lgn)时间. 在书架那个例子中,当书架上的书已经按照作者名字从左向右依次排好序后才开始进行查找.我们将使用作者名字作为主关键字,现在让我们搜索下由Jonathan Swift所写的书.你可能已经推测到:因为作者的姓以"S"开头,"S"是字母表中的第19个字母,且19/26与3/4接近,因此你可能会浏览书架上位置