我遇到了以下问题:
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));;
直观地说,我知道它是如何以及为什么有效,但我不确定它是否适用于所有可能的情况。那么如何正式证明呢?
编辑:是的,我忘记添加,列表已排序,抱歉。答案 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
......等等。