Московский государственный индустриальный университет
Опубликован: 27.09.2006 | Доступ: свободный | Студентов: 3333 / 380 | Оценка: 4.17 / 3.79 | Длительность: 24:17:00
Специальности: Программист
Лекция 8:

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

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

Расширение области значения переменной

Данный метод в значительной мере подобен разобранному в предыдущей секции — если необходимая переменная уже содержится в постусловии, то ее можно использовать для построения инварианта, просто разрешив ей меняться в более широких пределах.

В качестве примера разберем классическую задачу "Жулик на пособии". В оригинальном варианте рассматриваются три потенциально бесконечных упорядоченных по алфавиту списка фамилий — сотрудники исследовательского центра IBM, студенты Колумбийского университета и безработные Нью-Йорка. Требуется найти первую из фамилий, встречающуюся во всех трех списках (в предположении, что она есть).

Задача 8.5. Найдите минимальное число, содержащееся в каждом из трех упорядоченных по возрастанию массивов целых чисел, в предположении, что таковое существует.

Решение Пусть имеющиеся массивы — это a, b и c. Переменные, содержащие значения индексов для каждого из этих массивов, обозначим через i, j и k соответственно, а индексы искомого числа в каждом из них — iv, jv и kv.

Если не включать в предусловие информацию о том, что массивы являются упорядоченными (не забывая об этом, конечно), то пред- и постусловия искомой программы буду иметь вид Q=T, и R=((i=iv) \land (j=jv) \land (k=kv)).

Расширение области значений переменных i, j и k — естественный способ построения инварианта в данном случае: I= ((0\leqslant i \leqslant iv)
\land
(0\leqslant j \leqslant jv) \land (0\leqslant k \leqslant kv) ). В качестве ограничивающей функции можно взять h=iv-i+jv-j+kv-k, выбор начальных присваиваний S0 проблем тоже не вызывает — ясно, что операторы "i=0; j=0; k=0;" сделают инвариант истинным.

В качестве действий, которые будут приближать цикл к завершению можно использовать операторы "i++;", "j++;" и "k++;". При этом понятно, что каждый из них обязан присутствовать в итоговой программе.

Вычислим wp("i++;", I) = (i+1 \leqslant iv). Это означает, что увеличение индекса i в цикле нужно делать, когда i+1 \leqslant
iv. К сожалению, записать подобное условие в программе невозможно, так как переменной iv в ней просто может не быть!

Легко заметить, однако, что выполнение условия i+1 \leqslant
iv при истинном инварианте I означает, что число a[i] меньше искомого в задаче, а это может быть только при условии истинности дизъюнкции a[i] < b[j] \lor a[i] < c[k]. Аналогично заключаем, что если истинна дизъюнкция b[j] < a[i] \lor b[j] < c[k], то можно увеличивать значение индекса j, а при истинности предиката c[k] < a[i] \lor c[k] < b[j] — индекса k.

Это уже позволяет написать программу, но если внимательно исследовать доказательство ее правильности, то можно обнаружить возможность для ее упрощения, что и реализовано ниже.

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

public class Arr3 {
    public static void main(String[] args){    
        int a[] = { 1, 2, 4, 8,16,32,64,128};
        int b[] = {10,12,14,16,18,20,22, 24};
        int c[] = { 9,12,13,16,17,20,21, 24};
        int i = 0, j = 0, k = 0;
        while (true) { 
            if (a[i] < b[j]) {
                i++; continue;
            }
            if (b[j] < c[k]) {
                j++; continue;
            }
            if (c[k] < a[i]) {
                k++; continue;
            }
            Xterm.println("Минимальное общее число=" + a[i]);
            return;
        }
    }
}

Обязательно проверьте все пять условий правильности этой итоговой программы.

Задачи для самостоятельного решения

При решении задач необходимо построить и доказать правильность построенной программы вида "S0;while(e)S;", а при отсутствии в условии задачи явно заданных инварианта цикла и ограничивающей функции объяснить предварительно, каким образом они были получены.

Задача 8.6. Напишите программу, печатающую n -ое число Фибоначчи ( f_0=0, f_1=1, f_k=f_{k-1}+f_{k-2} \ \mbox{для $k>1$} ). При написании программы используйте Q=(n\in \mathbb{Z}_M \land n>0), R=(a=f_n), I=(1 \leqslant i \leqslant n \land a = f_i \land b = f_{i-1}), h=n-i. Число n в программе изменять нельзя.

Задача 8.7. Напишите программу, находящую частное q и остаток r от деления x на y, не использующую операций умножения и деления. При написании программы положите Q=(x\in \mathbb{Z}_M \land y\in
\mathbb{Z}_M \land x\geqslant 0 \land y>0), R=(0 \leqslant r < y \land
q y + r = x), I=(0 \leqslant r \land 0 < y \land q y + r = x), h=r-y+1. Величины x и y в программе изменять не разрешается.

Задача 8.8. Напишите программу, находящую наибольший общий делитель gcd(X,Y) двух целых положительных чисел X и Y, не использующую операций умножения и деления и не изменяющую величин X и Y. При написании программы положите Q=(X\in \mathbb{Z}_M \land Y\in \mathbb{Z}_M \land X>0 \land Y>0), R=(x=y=gcd(X,Y)), I=(0<x \land 0<y \land gcd(x,y)=gcd(X,Y)), h=x+y-2\cdot gcd(x,y).

Указание Воспользуйтесь следующими свойствами наибольшего общего делителя двух чисел не равных одновременно нулю (не забудьте научиться доказывать все эти свойства):

gcd(x,y)=gcd(x,y-x)=gcd(x-y,y),
gcd(x,y)=gcd(x,y+x)=gcd(x+y,y),
gcd(x,x)=x,
gcd(x,y)=gcd(y,x), gcd(x,0)=gcd(0,x)=x.

Задача 8.9. Напишите программу, находящую приближенное значение квадратного корня a \in 
\mathbb{Z}_M^+ из заданного неотрицательного целого числа 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^2\leqslant n. Оцените временную сложность получившейся программы и сравните ее со сложностью программы, построенной в задаче 8.1.

Задача 8.10. Напишите программу, определяющую первое вхождение заданного целого числа x в заданный массив массивов b[0..m-1][0..n-1] целых чисел ( m>0,
n>0 ). Значения элементов массива b и числа x, m и n в программе изменять нельзя. В момент завершения должно быть либо b[i][j] = x, либо, если числа x в массиве нет, i=m. Точные пред- и постусловия требуемой программы таковы: Q=(m>0 \land n>0), R=((0\leqslant i <m \land 0 \leqslant j < n \land x = b[i][j])\lor
(i=m \land x \notin b[0..m-1][0..n-1])).

Указание Используйте инвариант, утверждающий, что x не находится в уже проверенных строках b[0..i-1] и среди уже проверенных элементов b[i][0..j-1] текущей строки i. В качестве ограничивающей функции возьмите h=(m-i)\cdot n - j + m - i.

Задача 8.11. Напишите программу (бинарный или двоичный поиск), определяющую для упорядоченного по неубыванию массива b[0..n-1] целых чисел и заданного целого числа x позицию i, в которую может быть вставлено это число без нарушения упорядоченности массива. Точные пред- и постусловия требуемой программы, временная сложность которой не должна превосходить \Theta(\log n), таковы: Q=(x\in \mathbb{Z}_M \land n\in \mathbb{Z}_M \land n >0
\land (\forall j\ 0 \leqslant j < n-1\colon b[j] \leqslant b[j+1])), R=(
(i=-1\land x < b[0])\lor
(0\leqslant i < n-1\land b[i] \leqslant x < b[i+1])\lor
(i=n\land b[n-1] \leqslant x)
). При написании программы величины x, n и элементы массива b изменять не разрешается, для построения инварианта используйте метод замены константы переменной.

Задача 8.12. Напишите программу, печатающую факториал введенного неотрицательного целого числа, изменять которое нельзя. Для построения инварианта используйте метод замены константы переменной.

< Лекция 7 || Лекция 8: 12345 || Лекция 9 >
Анастасия Халудорова
Анастасия Халудорова
екатерина яковлева
екатерина яковлева