• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

无信不立

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【数据结构和算法】循环不变式的理解

一、什么是循环不变式

循环不变式(loop invariants)不只是一种计算机科学的思想,准确地说是一种数学思想。在数学上阐述了通过循环(迭代、递归)去计算一个累计的目标值的正确性。

比如插入排序,每次循环从数组A中取出第j个元素插入有序区A[1 .. j-1],然后递增j。这样A[1 .. j-1]的有序性始终得到保持,这就是所谓的“循环不变”了。

循环不变式主体是不变式,也就是一种描述规则的表达式。其过程分三个部分:初始,保持,终止。
  1、初始:循环的第一次迭代之前,它为真。
  2、保持:如果循环的某次迭代之前它为真,那么循环的下次迭代之前它仍为真。
  3、终止:在循环终止时,循环不变式为我们提供一个有用的性质,该性质有助于我们证明算法是正确的。 
  在这三个部分中,前两个是条件,最后一个是结论。

利用循环不变式(loop invariant)来证明循环的正确性与用数学归纳法(induction)证明数学等式的相同点在于:

都需要验证初值,或初始状态是否满足条件。
之后再证明在归纳或递推的过程中仍然满足这种条件。(这个条件在数学归纳中叫做递推关系,在循环中就是循环不变式(loop invariant))。

循环不变式(loop invariant)与数学归纳法(induction)的区别在于:

数学归纳可能是无限的,是无限递推的,但循环不变式所要证明的循环是要结束并给出正确结果的。

 

如何找循环不变式?

由于算法是一步步执行的,那么如果每一步(包括初试和结束)都满足一个共同的条件,那么这个条件就是要找的循环不变式(loop invariant)。

 

二、理解循环不变式

例子:

问题:给出二分查找的实现,函数原形 int seach(int[] array,int target)
array是保存已经从小到大排序好的数字,target是待查找的数字;
若查找成功则返回元素的下标,否则返回-1。


分析:二分查找的原理很简单:
把数组分成三份,中间一份只有一个元素,其他两份个数基本相同,如果中间的元素等于目标值,就返回它的下标;
如果大于,则去前半数组继续执行二分查找;
如果小于,则去后半数组;
直到数组没有元素为止。

这是循环不变式的一个计算机算法应用,我们可以按照它的规则来做,
我们的不变式就是:二分之后子数组的开始下标<=结束下标 
循环结束条件是:二分之后子数组的开始下标>结束下标 

二分查找的代码

package com.spring.test.service.algorithm.sort;

/**
 * 
 */
public class BinarySearch {


    public static void main(String[] args) {
        int[] arry={0,1,2,3,4,5,6};
        System.out.println(seach(arry,7));
    }


    /**
     *
     * @param array 目标数组
     * @param a  确认元素a是在目标数组中
     * @return 如果元素存在目标数组,则返回数组的下标,如果不存在,则返回-1
     */
    public static int seach(int[] array,int a){
        //检测初始时,不变式是否为真
        if(array==null || array.length==0){
            //数组为空,则不存在
            return -1;
        }

        //准备循环
        int leftIndex=0;//数组左端下标
        int rightIndex=array.length-1;//数组右端下标
        int middleIndex;//数组中间下标

        //循环开始
        while(leftIndex<=rightIndex){//rightIndex>=1 就是循环不变式

            middleIndex=(rightIndex+leftIndex)/2;//计算中间位置

            if(array[middleIndex]==a){//分支1:如果中间下标的元素等于目标元素

                return middleIndex;

            }else if(array[middleIndex]>a){//分支2:中间元素大于目标元素,则代表数据在左半部分数组

                rightIndex=middleIndex-1;//把数组右端的下标设置为中间元素下标的前一个

            }else{//分支3:中间元素小于目标元素,则代表数据在右半部分数组

                leftIndex=middleIndex+1;//把数组左端的小标设置为中间元素的下标的后一个
            }
        }
        return -1;
    }
}
View Code

 

posted on 2019-05-21 16:08  无信不立  阅读(1249)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3