Вы находитесь на странице: 1из 7

Теория алгоритмов

Практическая работа № 3. Нормальные


алгоритмы
Теоретические сведения

Основная операция при работе нормальных алгоритмов Маркова – переработка


слов в некотором алфавите. Эта переработка заключается в производстве
некоторого количества замен определенных последовательностей символов.
Данные замены совершаются в СТРОГО определенном порядке, а именно: после
каждой замены алгоритм читается с самого начала, а слово анализируется с самого
первого символа. В отличие от машин Тьюринга, алгоритмы Маркова выполняются
без какого-либо устройства, осуществляющего движения и имеющего внутреннюю
память. В данном случае мы можем оперировать только ленточными символами.
Сама лента в этом случае не разделяется на строгие ячейки, а имеет гибкую основу,
что позволяет ей растягиваться и сжиматься исходя из того, увеличивается в слове
число символов или уменьшается.

Программы формате алгоритмов Маркова

Условимся записывать команду в следующем виде:

{ai } → {bj }[∙]

где {ai } – последовательность символов, которая ищется в слове; → – знак


перехода к операции записи;

{bj } – последовательность символов, которая записывается вместо найденной;

[∙] – знак принудительного окончания алгоритма (необязательный параметр).

Программа (алгоритм) представляет собой совокупность строк указанного вида,


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

Например, алгоритм, состоящий из одной строки вида

0→∗

будучи примененным к слову в алфавите {0, 1} , заменит все нули на звездочки. В


свою очередь алгоритм

0 → ∗∙

будучи примененный к слову в алфавите {0, 1} , заменит на звездочку только


первый встреченный ноль.

Довольно сложная для реализации на машинах Тьюринга задача сортировки слова


по возрастанию, допустим в алфавите {0, 1, 2} , решается при помощи Маркова
весьма изящно:

20 → 02
10 → 01
21 → 12

Существует также специальный зарезервированный символ, именуемый «пустое


слово», он обозначается как Λ . Этот символ служит для обозначения пустого
слова, а также, по своему смыслу, содержится между всеми символами слова,
причем в неограниченном количестве. Таким образом, можно считать, что входная
строка abcde формально представима как ΛaΛbΛcΛdΛeΛ , причем при удалении
символа Λ посредством строки вида:
Λ→

ничего не изменится. Иными словами, добавление или удаление символа Λ


является совершенно бессмысленной процедурой, он как бы «прозрачный» и при
этом «самовоспроизводящийся». По сути, этот символ обозначает «пустоту»,
«ничто» и поэтому есть везде. Единственный практический смысл данного символа
проявляется тогда, когда необходимо написать вместо пустого слова какой-либо
символ или необходимо какой-либо символ написать строго в начале данного
слова. Оба эти действия выполняются с помощью команды

Λ→0

При составлении алгоритма следует обратить внимание на то, должно ли по


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

выбирать названия дополнительных элементов алфавита в соответствии с


логикой производимых замен или текущего действия;
по возможности не слишком сильно расширять имеющийся алфавит;
минимизировать количество строк в алгоритме.

Помимо этих, уже привычных правил следует обратить особое внимание на то, в
правильном ли порядке находятся строки алгоритма. Это один из важнейших
аспектов работы алгоритма и источник большинства ошибок. Как правило, схема
такая:

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


2. основное тело программы (алгоритма);
3. начальные условия работы алгоритма (добавление флага, поиск начала слова и
т.д.).

Разрабатывать такие программы проще «с конца», т.е. сначала пишется блок


инициализации (его место в программе скорее всего будет в самом низу), затем
основное тело (в большинстве случаев вставляется перед блоком инициализации), и
в конце дописываются строки окончания (они при этом будут в самом верху
программы, т.е. считываться при работе алгоритма первыми).

Пример решения задачи

A = {0, 1} . Заменить произвольное слово на символ «0».

Решение.

00 → 0 : два стоящих подряд нуля меняются на один, строчка работает до тех пор,
пока в слове все нули не станут одиночными;

1 → 0 : когда все нули станут одиночными, сработает данная строка – первая


единица заменится на ноль (после этого снова один раз сработает первая строка,
затем один раз вторая и т.д.

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

0 → : уничтожаем все нули;

1 → : уничтожаем все единицы;

Λ → 0 : на пустом месте ставим ноль.

Задачи для самостоятельной работы

Задача 1

A = {0, 1} . Удвоить все символы в слове.

Задача 2
A = {0, 1} . Удалить каждый третий символ.

Задача 3

A = {0, 1} . Поставить в конце слова « + », если в нем есть хотя бы один ноль,
поставить « − », если это не так.

Задача 4

A = {0, 1} . Переработать слово в « + », если в нем есть хотя бы два нуля


(неважно расположены они рядом или нет), и в « − », если это не так (т.е. исходное
слово удаляется).

Задача 5

A = {0, 1} . Поставить в конце слова « + », если в нем рядом встречаются хотя бы


два нуля (т.е. они соседние) и « − » если это не так.

Задача 6

A = {0, 1} . Переработать произвольное слово в « + », если в нем рядом


встречаются точно два нуля (т.е. они соседние) и больше нулей в слове нет и в « − »,
если это не так.

Задача 7

A = {0, 1} . Скопировать слово в прямом порядке после слова

Задача 8

A = {0, 1} . Скопировать слово в обратном порядке после слова

Задача 9
A = {1} . Вычислить сумму двух натуральных чисел в унарном коде. Результат
записать вместо слова.

Задача 10

A = {1} . Вычислить разность двух натуральных чисел в унарном коде. Результат


записать вместо слова