Инвариант цикла Дафни может не выполняться

Это простая задача разделения 0 и 1 в массиве. Я не могу понять, почему инвариант цикла не выполняется.

method rearrange(arr: array<int>, N: int) returns (front: int)
    requires N == arr.Length
    requires forall i :: 0 <= i < arr.Length ==> arr[i] == 0 || arr[i] == 1
    modifies arr
    ensures 0 <= front <= arr.Length
    ensures forall i :: 0 <= i <= front - 1 ==> arr[i] == 0
    ensures forall j :: front <= j <= N - 1 ==> arr[j] == 1
{
    front := 0;
    var back := N;
    while(front < back)
        invariant 0 <= front <= back <= N
        invariant forall i :: 0 <= i <= front - 1 ==> arr[i] == 0
        // The first one does not hold, the second one holds though
        invariant forall j :: back <= j < N ==> arr[j] == 1
    {
        if(arr[front] == 1){
            arr[front], arr[back - 1] := arr[back - 1], arr[front];
            back := back - 1;
        }else{
            front := front + 1;
        }
    }
    return front;
}

person tn2000    schedule 19.02.2020    source источник


Ответы (1)


При входе в ваш метод предварительное условие говорит вам

forall i :: 0 <= i < arr.Length ==> arr[i] == 0 || arr[i] == 1

Итак, на тот момент известно, что все элементы массива либо 0, либо 1. Однако, поскольку массив модифицируется циклом, вы должны упомянуть в инвариантах все то, что вы все еще хотите запомнить о содержимом массива.

Другими словами, чтобы убедиться, что тело цикла поддерживает инвариант, представьте, что тело цикла начинается в произвольном состоянии, удовлетворяющем инварианту. Вы можете иметь в виду, что элементы массива остаются 0 или 1, но ваш инвариант не говорит об этом. Вот почему вы не можете доказать, что инвариант цикла сохраняется.

Чтобы решить проблему, добавьте

forall i :: 0 <= i < arr.Length ==> arr[i] == 0 || arr[i] == 1

как инвариант цикла.

Рустан

person Rustan Leino    schedule 24.02.2020