【推理的思想】程序正确性证明(三):简化策略

在上一篇文章里,我们尝试用演绎推理的方式,证明了一个程序的正确性。

演绎推理过程https://blog.csdn.net/wkqyxyh/article/details/149800219?spm=1011.2415.3001.5331

示例中的程序非常简单,但是证明的过程已经比较复杂了,需要相当的技巧。可以想见,如果要对实际项目里的程序做正确性证明,难度会有多高。那怎么把这件事简化一下呢?主要有两种思路,一种是只证明程序的一部分是正确的,也就是说,只针对程序里的一些片段做证明,而不是针对整个程序。我们平常通过代码走查,明确程序里哪些地方肯定没问题,进而缩小后面测试的关注范围,就是这个思路。

另一种思路,是只证明“程序于期望的一部分而言是正确的”。什么意思呢?我们来看一个例子。假设被测程序的期望是:“对给定的数组,按元素从小到大的顺序重新排序”。

这是程序的实现:

public static int[] sort(int[] arr) {
    int temp = 0;
    for(int i = 0; i <arr.length - 1; i++) {
        for(intj = 0; j < arr.length-1-i; j++) {
            if(arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return arr;
}

这个程序可比算阶乘的程序复杂多了,证明起来会非常困难。

怎么办呢?我们把原始期望F做一次冗余分解,得到两个子期望:

F1的意思是“输出数组中的元素是从小到大排序的”,F2的意思是“输出数组是输入数组的重新排序”。我们只证明F2成立。这就很容易了,因为程序里除了交换两个元素的位置之外,没有对输入数组做任何其它的修改。

那只证明F2成立有什么用呢?不能证明F成立,到头来还是要用测试用例来验证。

但是,如果证明了F2成立,就可以大幅降低测试用例的执行成本。假设我们选了一个“包含10000个元素的数组”当测试用例,怎么做结果校验呢?我们需要做两件事:

  • 第一,我们要看输出数组里的元素是不是按从小到大排序的;

  • 第二,要看输出数组里的10000个元素,是不是跟输入数组里的元素一一对应。

显然,第二件事要比第一件事麻烦得多。但是,如果我们已经证明了F2成立,第二件事就不用做了。这样,测试结果校验就变得很容易。可见有的时候,只证明“程序于期望的一部分而言是正确的”,仍然是很有意义的一件事。这就是演绎推理在工程实践中的价值

我们讨论过,形式化证明并非解决正确性判定问题的灵丹妙药。已经被证明是“正确”的程序,仍然有可能出现错误。原因就在于,形式化证明往往需要基于一些简化设定。

形式化证明并非解决正确性判定问题的灵丹妙药。究其原因,是被测对象内在与外在的复杂性,致使我们对理想与现实进行形式化的过程中,难免引入各种简化设定。https://blog.csdn.net/wkqyxyh/article/details/149275881?spm=1011.2415.3001.5331如果我们只在理想与现实的“局部区域”应用演绎推理,就可以减少简化设定的引入,从而在一定程度上回避这种方法的局限性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值