如何证明以下代码的正确性?

时间:2014-11-17 20:04:32

标签: algorithm functional-programming ocaml

我遇到了以下问题:

Write a function, that, given a list l and an int c, returns min(|c+x1+x2|), where x1 and x2 are some values from the list. [It's also possible that x1=x2]

我有一个显然可以解决这个问题的代码:

let bestSum l c =
  let rec f l lr res =
    match (l,lr) with
    | ([],lr) -> res
    | (l,[]) -> res
    | (h1::t1, h2::t2) -> if (h1+h2+c)>0 then f l t2 (min res (h1+h2+c)) else
                            f t1 lr (min res (-(h1+h2+c))) in
  f l (rev l) (abs (2* (hd l) + c));;

直观地说,我知道它是如何以及为什么有效,但我不确定它是否适用于所有可能的情况。那么如何正式证明呢?

编辑:是的,我忘记添加,列表已排序,抱歉。

1 个答案:

答案 0 :(得分:1)

我将假设您的列表已排序,因为否则它不起作用。以下是您的算法的说明,应该清楚说明您的程序的工作原理。假设您的号码为x[0], x[1], ... , x[n]。目标是找到两个数字x[i]x[j],使其总和尽可能接近-c。让我们在x[i] + x[j] + c(n + 1)网格中绘制值(n + 1),用+标记正值,用-标记负值(请参阅下面的示例) 。目标是找到具有最小绝对值的单元格。

Example where c = 0, x = [-3, -2, 1, 4].

       x[0] x[1] x[2] x[3]

x[0]     -    -    -   (+)
x[1]     -    -   (-)  (+)
x[2]    (-)  (-)  (+)   + 
x[3]    (+)   +    +    + 

您会注意到-+分为两个区域,事实上必须如此,因为每个单元格的值都小于正下方的单元格或者在它的右边。基于该观察,唯一可以是最优的单元格是-+之间的边界。

您的算法基本上遵循此边界(算法考虑的单元格在括号中标记)。让我们走几步:

首先,考虑x[3] + x[0]。我们发现它是+,所以x[3] + x[1]不可能更好,接下来请尝试x[2] + x[0]

       x[0] x[1] x[2] x[3]

x[0]     ?    ?    ?    ?
x[1]     ?    ?    ?    ?
x[2]     *    ?    ?    ? 
x[3]    (+)   X    X    X 

* = thing to try next
X = ruled out

x[2] + x[0]是否定的,因此x[1] + x[0]不可能更好。接下来尝试x[2] + x[1]

       x[0] x[1] x[2] x[3]

x[0]     X    ?    ?    ?
x[1]     X    ?    ?    ?
x[2]    (-)   *    ?    ? 
x[3]    (+)   X    X    X 

x[2] + x[1]是否定的,因此x[1] + x[1]不能更好。接下来尝试x[2] + x[2]

       x[0] x[1] x[2] x[3]

x[0]     X    X    ?    ?
x[1]     X    X    ?    ?
x[2]    (-)  (-)   *    ? 
x[3]    (+)   X    X    X 

......等等。