英文:
Iterative algorithm and functional code (Dafny)
问题
Here's the translation of the code portions you provided:
我有这个简单的算法+函数:
method DifferenceMinMax(a: array<int>) returns (diff: int)
requires a.Length > 0
ensures diff == (Max(a[..]) - Min(a[..]))
{
var minVal := a[0];
var maxVal := a[0];
for i := 1 to a.Length
invariant 1 <= i <= a.Length
invariant minVal <= maxVal
invariant forall k :: 0 <= k < i ==> minVal <= a[k] && a[k] <= maxVal
{
if a[i] < minVal {
minVal := a[i];
} else if a[i] > maxVal {
maxVal := a[i];
}
}
diff := maxVal - minVal;
}
function Min(a: seq<int>) : int
requires |a| > 0
{
if |a| == 1 then a[0]
else if a[0] <= Min(a[1..]) then a[0] else Min(a[1..])
}
function Max(a: seq<int>) : int
requires |a| > 0
{
if |a| == 1 then a[0]
else if a[0] >= Max(a[1..]) then a[0] else Max(a[1..])
}
这不验证。
我知道我可以像这样重写方法:
method DifferenceMinMax(a: array<int>) returns (diff: int)
requires a.Length > 0
ensures diff == (Max(a[..]) - Min(a[..]))
{
var minVal := Min(a[..]);
var maxVal := Max(a[..]);
diff := maxVal - minVal;
}
这个验证。但这会两次遍历数组,而第一个迭代解决方案只会遍历一次。假设我真的想要第一个迭代算法。如何定义 Max 和 Min 以确保返回的 diff 真的是最大值和最小值之间的差异?
我已经尝试过这个,希望在循环不变式中以某种方式使用它,但它不会验证:
function Min(a: seq<int>) : (m: int)
requires |a| > 0
ensures forall n :: n in a ==> m in a && m <= n
{
if |a| == 1 then a[0]
else if a[0] <= Min(a[1..]) then a[0] else Min(a[1..])
}
我是否需要完全摒弃这些函数?谢谢。
I've provided the requested translations of the code portions. If you have any specific questions or need further assistance with this code, please feel free to ask.
英文:
I have this simple algorithm + functions:
method DifferenceMinMax(a: array<int>) returns (diff: int)
requires a.Length > 0
ensures diff == (Max(a[..]) - Min(a[..]))
{
var minVal := a[0];
var maxVal := a[0];
for i := 1 to a.Length
invariant 1 <= i <= a.Length
invariant minVal <= maxVal
invariant forall k :: 0 <= k < i ==> minVal <= a[k] && a[k] <= maxVal
{
if a[i] < minVal {
minVal := a[i];
} else if a[i] > maxVal {
maxVal := a[i];
}
}
diff := maxVal - minVal;
}
function Min(a: seq<int>) : int
requires |a| > 0
{
if |a| == 1 then a[0]
else if a[0] <= Min(a[1..]) then a[0] else Min(a[1..])
}
function Max(a: seq<int>) : int
requires |a| > 0
{
if |a| == 1 then a[0]
else if a[0] >= Max(a[1..]) then a[0] else Max(a[1..])
}
This doesn't verify.
I know I can rewrite the method like this:
method DifferenceMinMax(a: array<int>) returns (diff: int)
requires a.Length > 0
ensures diff == (Max(a[..]) - Min(a[..]))
{
var minVal := Min(a[..]);
var maxVal := Max(a[..]);
diff := maxVal - minVal;
}
This verifies. But this traverses the array twice, while the first iterative solution does it only once. Assume I really want the first iterative algorithm. How do I define Max and Min so that the returned diff is really the difference between the max and the min?
I already tried this, which I was hoping to use in the loop invariant somehow, but it doesn't verify:
function Min(a: seq<int>) : (m: int)
requires |a| > 0
ensures forall n :: n in a ==> m in a && m <= n
{
if |a| == 1 then a[0]
else if a[0] <= Min(a[1..]) then a[0] else Min(a[1..])
}
Do I need to get rid of the functions entirely?
Thanks.
答案1
得分: 2
以下是翻译好的部分:
建议
- 在你的循环中,你有效地计算了
min(min(min(a[0], a[1]), a[2]), a[3])
。看着这个,最佳实践是确保你的最小值的规定与这个计算相匹配。你当前的规定计算了min(a[0], min(a[1], min(a[2]....)))
,因此你将会很难证明这两者是相同的,或者需要一个引理来证明它们是相同的,或者后置条件(我还没有尝试过) - 如果一个for循环无法验证(即某些不变量),最好将其临时转换为while循环,并明确指定循环变量的赋值
- 将不变量编写为应在for循环结束时成立的断言
- 应用最弱前置条件演算法来查看开始时应该成立的内容
- 在你的min和max函数中,你两次计算相同的函数,你可以将它分配给一个变量
- 你需要有一个关于MinVal(a)和MaxVal(a)的不变量,它在最后产生了一些内容。
给出了上述建议,这是解决方案。
解决方案
method DifferenceMinMax(a: array<int>) returns (diff: int)
requires a.Length > 0
ensures diff == (Max(a[..]) - Min(a[..]))
{
var minVal := a[0];
var maxVal := a[0];
for i := 1 to a.Length
invariant 1 <= i <= a.Length
invariant minVal <= maxVal
invariant forall k :: 0 <= k < i ==> minVal <= a[k] && a[k] <= maxVal
invariant minVal == Min(a[..i])
invariant maxVal == Max(a[..i])
{
if a[i] < minVal {
minVal := a[i];
} else if a[i] > maxVal {
maxVal := a[i];
}
assert a[..i+1][..i] == a[..i];
}
assert a[..a.Length] == a[..];
diff := maxVal - minVal;
}
function Min(a: seq<int>) : (m: int)
requires |a| > 0
{
if |a| == 1 then a[0]
else
var minPrefix := Min(a[..|a|-1]);
if a[|a|-1] <= minPrefix then a[|a|-1] else minPrefix
}
function Max(a: seq<int>) : (m: int)
requires |a| > 0
{
if |a| == 1 then a[0]
else
var maxPrefix := Max(a[..|a|-1]);
if a[|a|-1] >= maxPrefix then a[|a|-1] else maxPrefix
}
请注意,由于原始文本中包含了HTML编码(例如<
和>
),这些编码在翻译中被保留了下来。如果需要,您可以将其还原为正常的HTML符号。
英文:
Great question.
Here is some advice on how to solve such an issue, and below is the solution
Advice
- In your loop, you effectively compute
min(min(min(a[0], a[1]), a[2]), a[3])
. Looking at that, it is best practice to ensure your specification of the minimum matches this computation. Your current spec computesmin(a[0], min(a[1], min(a[2]....)))
so you'll have a hard time (meaning, needing a lemma) to prove that the two are the same, or postconditions (I haven't tried that) - If a for loop is failing to verify (i.e. some invariants), it's better to convert it temporarily to a while loop and explicit the assignments of the loop variable
- Write the invariant as an assertion that should hold at the end of the for loop
- Apply the weakest precondition calculus to see what should hold at the beginning
- In your min and max function, you compute the same function twice, you can assign it to a variable
- You need to have an invariant that produces something about MinVal(a) and MaxVal(a) at the end.
That advice given, here is the solution.
*** Solution ***
method DifferenceMinMax(a: array<int>) returns (diff: int)
requires a.Length > 0
ensures diff == (Max(a[..]) - Min(a[..]))
{
var minVal := a[0];
var maxVal := a[0];
for i := 1 to a.Length
invariant 1 <= i <= a.Length
invariant minVal <= maxVal
invariant forall k :: 0 <= k < i ==> minVal <= a[k] && a[k] <= maxVal
invariant minVal == Min(a[..i])
invariant maxVal == Max(a[..i])
{
if a[i] < minVal {
minVal := a[i];
} else if a[i] > maxVal {
maxVal := a[i];
}
assert a[..i+1][..i] == a[..i];
}
assert a[..a.Length] == a[..];
diff := maxVal - minVal;
}
function Min(a: seq<int>) : (m: int)
requires |a| > 0
{
if |a| == 1 then a[0]
else
var minPrefix := Min(a[..|a|-1]);
if a[|a|-1] <= minPrefix then a[|a|-1] else minPrefix
}
function Max(a: seq<int>) : (m: int)
requires |a| > 0
{
if |a| == 1 then a[0]
else
var maxPrefix := Max(a[..|a|-1]);
if a[|a|-1] >= maxPrefix then a[|a|-1] else maxPrefix
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论