Опубликован: 27.09.2006 | Уровень: для всех | Доступ: платный | ВУЗ: Московский государственный индустриальный университет
Лекция 8:

Проектирование цикла при помощи инварианта

< Лекция 7 || Лекция 8: 12345 || Лекция 9 >

Замена константы переменной

В качестве первого примера использования этого метода построения инварианта рассмотрим простую задачу суммирования элементов массива.

Задача 8.3. Напишите программу, находящую сумму s элементов заданного целочисленного массива b[0..n-1], элементы которого и величину n изменять нельзя. Точные пред- и постусловия: Q = (n>0),

\displaystyle R=\left(s = \sum_{j=0}^{n-1} b[j]\right).

Решение В постусловие входит константа n, которую мы можем заменить новой переменной i, меняющейся в диапазоне от 0 до n включительно. Таким образом, метод замены константы переменной приводит нас к инварианту

\displaystyle I=\left(0\leqslant i \leqslant n
\land
\left(s = \sum_{j=0}^{i-1} b[j]\right)\right).
Понятно, что в качестве ограничивающей функции следует взять h=n-i.

Истинности инварианта легко добиться обнулением величин i и s, поэтому искомая программа будет иметь вид "i=0;s=0;while(i<n)S;" с неизвестным нам пока телом цикла S.

Для того, чтобы цикл завершился, необходимо уменьшать h, что вполне естественно делать, увеличивая i на единицу на каждой итерации цикла. Используя инвариант, находим, что вторым необходимым действием является добавление к s значения b[i]:

Текст программы

public class SumArr {
    static int b[];
    public static void main(String[] args) throws Exception { 
        int n = Xterm.inputInt("n -> ");
        b = new int[n];  
        for (int k=0; k<n; k++)
            b[k] = Xterm.inputInt("b["+k+"] -> ");
        int i=0, s=0;
        while (i < n) {
            s += b[i]; 
            i += 1;
        }
        Xterm.println("s = " + s);
    }
}

Докажем ее правильность.

  1. Так как
    \displaystyle wp("i=0;s=0;",
\left(0\leqslant i \leqslant n \land
\left(s = \sum_{j=0}^{i-1} b[j]\right)\right) =
\\
(0 \leqslant n \land T) = (0 \leqslant n),
    то (Q \Rightarrow wp(S0, I)) =
(n \leqslant 0 \lor 0 \leqslant n) = T.
  2.  

    $\displaystyle wp(S, I) = wp\left("s+=b[i];i+=1;",
\left(0\leqslant i \leqslant n \land
\left(s = \sum_{j=0}^{i-1} b[j]\right)\right)\right) =
wp\left("s+=b[i];",
\left(0\leqslant i+1 \leqslant n \land
\left(s = \sum_{j=0}^{i} b[j]\right)\right)\right) =
\Bigl(0\leqslant i+1 \leqslant n\ \land$

    $\displaystyle \left(s+b[i] = \sum_{j=0}^{i} b[j]\right)\Bigr) =
\left(-1\leqslant i \leqslant n-1 \land
\left(s = \sum_{j=0}^{i-1} b[j]\right)\right).$

    Теперь вычислим

    \displaystyle (I\land e \Rightarrow wp(S, I)) =
(i<0)\lor(i>n)\lor 
!\left(s = \sum_{j=0}^{i-1} b[j]\right)\lor 
\\
(i \geqslant n) \lor
\left(-1\leqslant i \leqslant n-1 \land
\left(s = \sum_{j=0}^{i-1} b[j]\right)\right)

    Данный предикат заведомо истинен, если истинен один из первых четырех его дизъюнктивных членов. В противном случае имеем 0 \leqslant i <n и I=T, поэтому истинен пятый его дизъюнктивный член, следовательно предикат является тавтологией.

  3. \displaystyle (I \land !e) = \left(0\leqslant i \leqslant n \land
\left(s = \sum_{j=0}^{i-1} b[j]\right)\right)\land (i \geqslant n) =
\left(s = \sum_{j=0}^{n-1} b[j]\right) \land (i=n)= (R \land (i=n))

    Очевидно, что (I \land !e \Rightarrow R).

  4. (I \land e \Rightarrow h > 0) = (!I \lor !e \lor h >0) =
(!I\lor(i\geqslant n \lor n > i)) = (!I \lor T) = T.
  5.  

    wp("h1=h; S;", h<h1) =
wp("h1=n-i;",wp("s+=b[i];i+=1;",n-i<h1)) =
\\
wp("h1=n-i;",n-i-1<h1)=(n-i-1<n-i)=T.

Следовательно, (I\land e \Rightarrow wp("h1=h; S;", h<h1))=
(I\land e \Rightarrow T) = (!I \lor !e \lor T) = T.

Построим более быструю программу нахождения приближенного значения квадратного корня.

Задача 8.4. Напишите программу, находящую приближенное значение квадратного корня a \in 
\mathbb{Z}_M^+ из заданного неотрицательного целого числа n. Точные пред- и постусловия требуемой программы, временная сложность которой не должна превосходить \Theta(\log n), таковы: Q=(n\in \mathbb{Z}_M \land n
\geqslant 0), R= (a \in \mathbb{Z}_M \land a \geqslant 0 \land a^2\leqslant n
\land (a + 1)^2 > n). При написании программы величину n изменять нельзя.

Решение Построим инвариант с помощью метода замены константы a+1 на переменную b. Из условия задачи вытекает, что a < b \leqslant n+1, следовательно инвариантом является предикат I=(a < b \leqslant n+1 \land
a^2\leqslant n < b^2). Условие продолжения цикла легко получается из того факта, что предикат (I \land !e \Rightarrow R) должен быть тавтологией — e = (a+1\ne b), а это означает использование ограничивающей функции h=b-a-1.

После присваиваний "a=0;b=n+1;" предикат I становится истинным, следовательно наша программа имеет вид "a=0;b=n+1;while(a+1!=b)S;" с неизвестным пока телом цикла S.

Для того чтобы цикл завершился, необходимо уменьшать h, что эквивалентно сближению чисел a и b. Уменьшение разности b-a на единицу на каждой итерации цикла не позволит достичь требуемой в условии задачи эффективности программы. Нужная временная сложность может быть получена при использовании метода деления отрезка [a,b] пополам на каждой итерации и выборе той из половинок, на которой лежит искомое приближенное значение квадратного корня. Реализация данной идеи приводит к следующей программе.

Текст программы

public class Sqrt3 {
    public static void main(String[] args) throws Exception {
        int a, b, n;
        n = Xterm.inputInt("n -> ");
        a = 0; 
        b = n+1;
        while (a+1 != b) {
            int c = (a+b)/2;
            if (c*c <= n) a = c;
            else          b = c;
        } 
        Xterm.println("sqrt(" + n + ") = " + a);
    }
}

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

< Лекция 7 || Лекция 8: 12345 || Лекция 9 >
Анастасия Халудорова
Анастасия Халудорова
екатерина яковлева
екатерина яковлева
Ирина Тютрина
Ирина Тютрина
Россия, Усолье-Сибирское
Глеб Бочкарёв
Глеб Бочкарёв
Россия