嵌套循环中的索引越界错误

时间:2017-07-27 19:34:47

标签: dafny

有人可以告诉我为什么这段代码会在input[i*cols + j]上生成“索引越界”错误吗?

method foo(input: array<int>, rows:int, cols:int)
requires input != null 
requires rows > 0 && cols > 0
requires rows * cols == input.Length
{
   var i := 0;
   while i < rows
   {
     var j := 0;
     while j < cols
     {
       var s := input[i*cols + j];
       j := j + 1;
     }
     i := i + 1;
   }
}

2 个答案:

答案 0 :(得分:1)

你的程序没有错误,但是Dafny很难证明这一点。

为了证明指数在界限内,Dafny需要证明:

i*cols + j < rows * cols

给出i < rowsj < cols(以及其他一些事实)。

此公式是非线性的(意味着它包含两个变量的乘法)。一般来说,这样的公式不在于可判定的逻辑片段,而在实践中,这意味着Dafny背后的解算器无法有效地推理它们。

现在,事实上,这个公式是正确的。这只是解算器无法理解原因。我们可以通过将证据分解为更小的步骤来帮助解决这个问题。

以下是验证的程序的完整版本。

lemma lemma_mul_le(x: int, y: int, z: int)
  requires 0 <= z
  requires x <= y
  ensures x * z <= y * z
{}

method foo(input: array<int>, rows:int, cols:int)
  requires input != null 
  requires rows > 0 && cols > 0
  requires rows * cols == input.Length
{
   var i := 0;
   while i < rows
   {
     var j := 0;
     while j < cols
     {
       lemma_mul_le(i, rows-1, cols);
       var s := input[i*cols + j];
       j := j + 1;
     }
     i := i + 1;
   }
}

我已经介绍了一个引理,它针对任何xyz,如果0 <= zx <= y则{{1} }。 Dafny能够在没有任何进一步帮助的情况下证明这一点。 (我们称之为“开合支撑证明”!)

然后,我将x * z <= y * z正文中的引理称为fooxy的某些特定值。我通过手工处理索引的证明来选择这些值。

Dafny能够验证引理是否为真,并且在给定引理的情况下,访问是在界限内。这导致程序没有错误。万岁!

有人可能会怀疑:Dafny如何能够在没有任何帮助的情况下证明这个引理,但它无法证明原始程序?

这是一个值得怀疑的事情。这些奇迹是为自动验证付出的代价。通常,可能存在多种等效方式来制定逻辑查询,该逻辑查询与求解器具有完全不同的性能。有一种艺术可以诱使求解者进行竞标。

在该程序的特定情况下,考虑它的一种方法是根据Dafny将发送给求解器的查询。为了验证z,Dafny将向具有“范围内”所有相关变量和事实的求解器发送查询。当被要求证明看似简单的非线性算术查询时,这可能导致求解器被侧向跟踪或以其他方式混淆。通过将违规公式分解为引理,我们基本上强迫求解者专注于困难部分,通过消除在推理foo时范围内的所有无关事实。在这种情况下,事实证明这足以让证据通过。在更困难的情况下,可能需要其他技巧。

答案 1 :(得分:-3)

如果row = 3且column = 4,那么我们可以继续并以口头方式运行它。在最后一步它将是     (行)* cols + cols

但是您的代码将被编入行*列

的索引