Опубликован: 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 >
Анастасия Халудорова
Анастасия Халудорова
подавляющее большиство фукций на пространстве последовательостей?
екатерина яковлева
екатерина яковлева
как получить сертификат,что для этого нужно?
Дмитрий Карпов
Дмитрий Карпов
Россия, Нижний Новгород
Антон Никитин
Антон Никитин
Россия, Хабаровск