Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Теоретическая информатика. Доказательство правильности. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
Пример 4.3. Возьмем рекурсивную программу, применимую к лю-
бой паре неотрицательных целых X1, Х2:
F(Х1, Х2) IF X1 = 0 ТНЕN 0
ELSE OTHERWISE F(0, F(Х1, Х2))
Проследим за вычислением F(1, 1), используя два различных пра­вила вычислений. По первому правилу мы будем стараться сначала заканчивать вычисления, относящиеся к внешнему обращению. По второму правилу мы будем отдавать предпочтение самому внутренне­му
обращению:
1) F(1, 1) = F(0, F(1, 1)) [Так как условие X1 = 0 ложно.] = 0 [Так как условие X1 = 0 истинно
для внешнего обращения к F.]
2) F(1, 1) = F(0, F(1, 1)) [Так как условие X1 = 0 ложно.] = F(0, F(0, F(1, 1))) [Так как условие X1 = 0
для внутреннего обращения ложно.]
= F(0, F(0, F(0, F(1, 1)))) = = F(0, F(0, F(0, F(0, F(1,1))))) и т. д.
Таким образом, в первом случае последовательность вычислений F(1, 1) конечна и F(1, 1) = 0. Во втором случае вычисления не закан- чиваются и, следовательно, значение F(1, 1) не
определено. Однако
отметим, что если для каких-либо аргументов в обоих случаях вычис­ления заканчиваются, то результат будет одним и тем же. Например, F(0, М) = 0 независимо от применяемых правил вычислений.
Итак, чтобы сделать наш язык программирования точным, нам нужно задать правила вычислений для программ, определяющие по­следовательность вычислений. В настоящей главе
мы будем считать, что правило вычислений отдает предпочтение самому левому и са- мому внутреннему обращению. Другими словами, в любой из момен­тов вычислений первым начинает вычисляться левое и внутреннее обращение (для простоты далее везде будем опускать слово «самое») к функции F, все аргументы которой уже не содержат F. Это прави-
41
ло – не обязательно наилучшее, иногда оно может приводить к неоканчивающейся последовательности вычислений, хотя другое правило дало бы конечную последовательность (пример 4.3). Однако во многих существующих языках программирования используется правило вычислений, подобное описанному выше. Кроме того, как мы уже отмечали, если по этому правилу вычисления заканчиваются, то результат будет тем же, что и
при других правилах, приводящих к окончанию. Большинство рассматриваемых нами программ заканчи­вается при любых значениях аргументов независимо от того, какие правила вычислений мы используем, а следовательно, и результат в любых случаях будет одинаковым. Однако для определенности все же будем считать, что предпочтение отдается левому внутреннему обращению.
Многие из приводимых в
этой главе примеров относятся к работе со списками. В таких программах мы используем некоторое подобие «лисповской» нотации. По этой нотации список – набор объектов, от­деленных друг от друга пробелами и заключенных в квадратные скоб­ки [ ]. Объектами, входящими в такие списки, являются атомы или другие списки. Атом – строка буквенно-цифровых символов,
не со­держащая пробелов. Мы будем считать, что атомы должны начинаться с одной из букв А, В, С, D или F, а переменные – с букв, отличных от этих букв. Такой прием позволяет нам при написании программ разли­чать переменные и атомы и обходиться без более общей функции QUOTE, используемой в Лиспе. Например, [А
В С] – список из трех элементов, каждый из которых есть атом; [А [В А [С]] В С] – список, в котором (на верхнем уровне) четыре элемента. Второй элемент – сам список [В А [С]]. Третий элемент этого списка – опять список [С]. Для пустого списка, т. е. списка, не содержащего ни одного элемента, мы выделяем специальное имя NIL. Кроме того, мы будем использовать следующие проверки и функции.
1. Проверка
= может применяться как к спискам, так и к числам.
Например, проверка [А В] = [А В] истинна, а проверки [А В] = [В А],
[А В] = [А [В]], В = А, А = NIL ложны.
2. Проверка АТОМ (X) применима к любым объектам, будь то
атомы или списки. АТОМ (X) дает значение TRUE (истина), если X – атом или пустой
список. Если X – непустой список, то АТОМ (X) дает
значение FALSE (ложь).
42
3. Функция CAR (L) применима к любому непустому списку. В ка-
честве результата выступает первый (на верхнем уровне) элемент этого списка. Примеры:
CAR ( [А В С] ) дает А. CAR ( [[А В] С] ) дает [А В]. CAR (NIL) не определено. CAR (А) не определено.
4. Функцию CDR(L) можно применять к любому непустому спис-
ку. Результатом является список, полученный из L путем
отбрасыва-
ния первого его элемента (на верхнем уровне). Примеры: CDR ( [А В С] ) дает [В С]. CDR ( [[А В] С] ) дает [С]. CDR ( [В] ) дает NIL (или [ ] ). CDR (NIL) не определено. CDR (А) не определено.
5. Функцию CONS(Х, L) можно применять к любому списку L и
любому X, будь то атом или список. В результате получается новый список: первый
его элемент есть X, а потом идут элементе списка L.
Примеры:
CONS ( А, [В С] ) дает [А В С].
CONS (А, NIL) дает [А].
CONS ( [А В], [В [С]] ) дает [[А В] В [С]]. CONS (А, В) не определено.
В некоторых наших примерых мы будем использовать и специаль-
ные атомы ТRUЕ и FALSE.
Пример 4.4. Как пример использования новой нотации приведем
рекурсивную программу, определяющую, является ли X элементом списка L (на верхнем уровне):
МЕМВЕR (Х, L) IF L = NIL ТНЕN FALSE
ЕLSЕ IF Х = САR (L) ТНЕN TRUE ЕLSЕ ОТНЕRWISЕ МЕМВЕR (X, CDR (L))
Проследим, как идет процесс вычислений по такой программе для
фактических аргументов С и [А В С D]:
МЕМВЕR (С, [А В
С D] ) = МЕМВЕR (С, [В С D] ) =
= МЕМВЕR (С, [С В] ) = TRUЕ
43
Теперь проследим, как проходит вычисление при фактических ар-
гументах С и [А В [С D]]:
МЕМВЕR (С, [А В [С D]]) = МЕМВЕR (С, [В [С D]] ) =
= МЕМВЕR (С, [[С В]] ) = МЕМВЕR (С, NIL) = FALSE
Пример 4.5. Напишем рекурсивную программу, добавляющую
список L1 к списку L2, другими словами, в этом списке, состоящем из двух, элементы первого стоят перед элементами второго:
APPEND (L1, L2) IF L1 = NIL ТНЕN L2
ЕLSЕ OTHERWISE CONS (CAR (L1), АРРЕND (CDR(L1), L2))
Проследим, как идет процесс вычислений при различных парах ар-
гументов: АРРЕND ([А В], [С D]) =
= CONS (САR ([А В]), АРРЕND (CDR ([А В]), [С D]))) = = СОNS (А
, АРРЕND ([В], [С D] )) = СОNS (А, СОNS (CAR ([В]),
АРРЕND (CDR([В]), [С D]))) =
= CONS (А, СОNS (В, АРРЕND (NIL, [С D] ))) = = СОNS (А, СОNS (В, [С, D])) = СОNS (А, [В С D]) = [А В С D];
АРРЕND ([[А]], [В С] ) = СОNS ([А], АРРЕND (NIL, [В С]) =
= СОNS ([А], [В С]) = [[А] В С],
АРРЕND([А В], NIL) = СОNS (А, АРРЕ
ND ([В], NIL)) = = СОNS (А, СОNS (В, АРРЕND (NIL, NIL))) = = СОNS (А, СОNS ( [В] ) = СОNS (А, [В] ) = [А В].
4.2. СТРУКТУРНАЯ ИНДУКЦИЯ
Рекурсивные программы обычно строятся по следующей схеме: сначала в них явно определяется способ вычисления результата для наипростейших значений аргументов рекурсивной функции, затем способ вычисления результата для аргументов более сложного вида,
44
причем используется сведение к вычислениям с данными более про­стого вида (с аргументами рекурсивного обращения к той же функ­ции). Таким образом, вполне естественно попытаться доказать пра­вильность таких программ следующим образом:
1) доказать, что программа работает правильно для простейших
данных (аргументов функции);
2) доказать, что программа работает правильно для более сложных данных (аргументов функции) в предположении, что она работает пра­вильно для более простых данных (аргументов функции).
Такой способ доказательства правильности для рекурсивных функ­ций естествен, поскольку он следует основной схеме вычислений в рекурсивных программах. Студенты уже, конечно, обратили внимание на то, что этапы 1 и 2 фактически являются этапами доказательства с помощью
индукции, но индукция осуществляется «по структуре» дан­ных, которые обрабатывает программа. В частности, если можно сопо­ставить интуитивное понятие более простых данных (допустимых ар­гументов функции) и более сложных с отношением во вполне упоря­доченном множестве значений данных (допустимых аргументов функ­ции), то этапы 1 и 2 будут аналогичны этапам доказательства правиль­ности
работы программы для всех допустимых аргументов с помощью метода обобщенной индукции. Предложенный выше способ доказа­тельства правильности назовем
турной индукции
. Проиллюстрируем этот метод на нескольких очень
доказательством с помощью струк-
простых рекурсивных программах.
Пример 4.6. Докажем правильность программы (разд. 4.1, при-
мер 4.1):
F(Х) IF X = 1 ТНЕN 1
ЕLSЕ ОТНЕRWISЕ X•F(Х – 1)
Предполагается, что эта функция вычисляет факториал от аргумен-
та. Нужно доказать, что F(М) = 1  2  3    N = N! для любого поло­жительного числа N. При доказательстве с помощью структурной индукции используем простую индукцию по положительным целым числам:
1) докажем, что F(1) = 1!. Действительно, F(1) = 1 = 1!;
2) докажем (для любого положительного числа N), что если
F(N) = 1 2    N = N!, то F(N + 1) = 1 2    N (N + 1) = (N + 1)!. Следовательно, мы предполагаем, что N – положительное число,
45
а F(N) = N! – гипотеза индукции. Так как N положительное число, то проверка N + 1 = 1 дает отрицательный ответ, и, прослеживая далее последовательность вычислений, получаем
F(N + 1) = (N + 1) F((N + 1) – 1) = (N + 1) F(N) = (по гипотезе индукции), = (N +1) (N!) = (N +1) (1 2    N) = 1 2    N (N + 1) = (N + 1)!
что и требовалось доказать, т. е. F(N) = N! для любого положительно­го числа N.
Пример 4.7. Докажем правильность программы (разд. 4.1, пример
4.4):
МЕМВЕR (Х, L) IF L = NIL THEN FALSЕ,
ЕLSЕ IF Х = CAR(L) ТНЕN TRUE, ЕLSЕ ОТНЕRWISЕ МЕМВЕR (X, CDR(L)).
Эта программа применима для любого элемента X и любого списка L. Предполагается, что она дает значение ТRUЕ, если X входит в каче­стве элемента верхнего уровня в список L; в противном же случае она дает значение FALSЕ:
МЕМВЕR (Х, L) = TRUE, если X элемент списка L,
= FALSЕ в других случаях.
Для доказательства правильности работы этой программы исполь­зуем структурную индукцию. Анализ программы показывает, что при рекурсивном обращении к МЕМВЕR из третьей строки программы только второй аргумент обращения выглядит проще, чем ранее. Таким образом, естественно вести индукцию
только по второму аргументу функции. При рекурсивном обращении к МЕМВЕR ее второй аргумент CDR (L) представляет собой список, который содержит на один эле­мент (на верхнем уровне) меньше, чем список L. Следовательно, в структурной индукции можно использовать простую индукцию по числу элементов в списке L. Так как это число всегда неотрицательно, то доказательство основывается
на простой индукции по неотрица-
тельным целым числам. Итак, нужно:
1) доказать, что для любого списка, содержащего нуль элементов,
МЕМВЕR работает правильно. Поскольку список, имеющий нуль эле-
46
ментов, это просто пустой список NIL, то очевидно, что МЕМВЕR (Х, NIL) = FALSЕ, так как X не есть элемент списка NIL;
2) доказать (для любого целого числа N), что если программа
МЕМВЕR правильно работает для всех списков L, содержащих N элементов (на верхнем уровне), то она будет правильно работать и для всех списков L, содержащих на верхнем уровне
(N + 1) элементов. По-
этому предположим, что МЕМВЕR правильно работает для списков L длиной N, т. е.
МЕМВЕR (Х, L) = TRUE если X есть элемент из L, = FALSЕ в других случаях. Это гипотеза индукции. Предположим, что L есть список, содер-
жащий (N + 1) элементов. Так как (N + 1)  1, то L  NIL. Прослеживая выполнение функции, видим, что
МЕМВЕR (Х, L) = TRUE если X = CAR (L),
= МЕМВЕR (Х, CDR (L)) в других случаях.
Если X = CAR(L) (а мы знаем, что CAR(L) определено, так как
L NIL), то X – элемент списка L и, следовательно, значение TRUЕ есть именно то значение, которое требуется. Если Х CAR (L), то X будет элементом списка L, если и только если X будет элементом CDR (L). (Функция CDR (L) определена, так как L NIL.) Однако CDR (L) представляет собой список
из N элементов, и по гипотезе индукции следует, что МЕМВЕR (Х, СDR (L)) будет правильно вычислять зна­чение TRUE или FALSЕ в зависимости от того, является ли X элемен­том CDR (L) или нет. Таким образом, если Х CАR (L), то МЕМВЕR (Х, L) = МЕМВЕR (Х, CDR (L)), а последняя функция вычисляет зна­чение либо TRUЕ, либо FALSЕ в
зависимости от того, будет ли X эле­ментом CDR (L), а следовательно, элементом списка L или нет, что и требовалось доказать.
4.3. ЗАДАНИЯ ДЛЯ САМОСТОЯТЕЛЬНОЙ РАБОТЫ
1. Проследите, как выполняется рекурсивная программа, реализу­ющая функцию Аккермана, при следующих аргументах: а) А(1, 2); б) А(1, 1); в) А(2, 1).
47
2. Для рекурсивной программы, применимой к любым двум спис­кам L1, L2:
INT (L1, L2) IF L1 = NIL ТНЕN NIL ЕLSЕ IF МЕМВЕR (САR (L1), L2) ТНЕN CONS (САR (L1), INT (CDR (L1), L2)) ЕLSЕ ОТНЕRWISЕ INT (CDR (L1), L2). Выпишите последовательность вычислений для INT ( [D C B],
[А В С]).
3. Рассмотрите рекурсивную программу, применимую для любых неотрицательных целых чисел N:
F(N) IF N = 0 ТНЕN 1
ЕLSЕ ОТНЕRWISЕ 2 • F(N – 1),
выведите формулу для вычисляемой
функции F(N) и докажите ее пра-
вильность.
4.4. КОНТРОЛЬНЫЕ ВОПРОСЫ К ГЛАВЕ 4
Тема «Рекурсивные программы. Структурная индукция»
1. Что такое рекурсивная программа?
2. В чем заключается принцип структурной индукции?
3. С какой целью применяется структурная индукция?
4. Можно ли использовать структурную индукцию для доказатель­ства правильности нерекурсивных программ?
48
БИБЛИОГРАФИЧЕСКИЙ СПИСОК
1. Андерсен Р. Доказательство правильности программ. – М.: Мир, 1982. – 163 с.
2. Веретельникова Е.Л. Доказательство правильности программ. Метод индуктивных утверждений: конспект лекций. – Новосибирск, Изд-во НГТУ,
2004. – 59 c.
3. Веретельникова Е.Л. Доказательство правильности программ. Допол- нительные приемы: конспект лекций. – Новосибирск: Изд-во НГТУ, 2005. – 36 с.
4. Веретельникова Е.Л. Сборник задач по курсу «
процессов»: Часть 1. Доказательство правильности программ: сборник за­дач. – Новосибирск: Изд-во НГТУ, 2006. – 48 с.
5. Романов Е.Л. Практикум по программированию на С++: учеб. посо­бие. – СПб.: БХВ-Петербург; Новосибирск: Изд-во НГТУ, 2004. – 432 с.
Теория вычислительных
49
ОГЛАВЛЕНИЕ
Глава 1. МАТЕМАТИЧЕСКАЯ ИНДУКЦИЯ ................................................................. 3
1.1. Простая индукция .................................................................................................. 3
1.2. Строгая математическая индукции .................................................................... 6
1.3. Задания для самостоятельной работы ................................................................ 9
1.4. Контрольные вопросы к главе 1. Тема «Математическая индукция» ......... 9
Глава 2. ДОКАЗАТЕЛЬСТВО ПРАВИЛЬНОСТИ БЛОК-СХЕМ
ПРОГРАММ ......................................................................................................... 10
2.1. Основные принципы доказательства правильности блок-схем ................ 11
2.2. Метод индуктивных утверждений .................................................................. 21
2.3. Задания для самостоятельной работы ............................................................. 29
2.4. Контрольные вопросы к главе 2. Тема 1. «Доказательство пра-
вильности блок
блок-схем методом индуктивных утверждений» ........................................ 30
Глава 3. ДОКАЗАТЕЛЬСТВО ПРАВИЛЬНОСТИ ПРОГРАММ,
НАПИСАННЫХ НА ОБЫЧНЫХ ЯЗЫКАХ
ПРОГРАММИРОВАНИЯ ................................................................................. 31
3.1. Основные приемы доказательства правильности программ ...................... 31
3.2. Задания для самостоятельной работы ............................................................. 36
3.3. Контрольные вопросы к главе 3. Тема «Доказательство правиль-
ности программ» ................................................................................................ 37
Глава 4. ДОКАЗАТЕЛЬСТВО ПРАВИЛЬНОСТИ РЕКУРСИВНЫХ
ПРОГРАММ ......................................................................................................... 37
4.1. Упрощенный язык программирования для иллюстрации понятия
рекурсии ....................................................................................................................... 38
4.2. Структурная индукция ....................................................................................... 44
4.3. Задания для самостоятельной работы ............................................................. 47
4.4. Контрольные вопросы к главе 4. Тема «Рекурсивные программы.
Структурная индукция» ................................................................................... 48
Библиографический список ............................................................................................... 49
-схем». Тема 2. «Доказательство правильности
50
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]