Соотношение с алгоритмами в интуитивном смысле
Алгоритмы в интуитивном смысле — это не математические объекты. Судить о них можно лишь на основании практического опыта и интуиции. Никакие математические доказательства к ним неприменимы.
Практический опыт в деле сопоставления широких формальных алгоритмов и интуитивных алгоритмов еще не очень велик, но, пожалуй, уже достаточен для того, чтобы высказать основной тезис широкой формальной теории алгоритмов:
Каков бы ни был алгоритм в интуитивном смысле над формальным языком L, если его запись может быть признана конструкцией, то существует эквивалентный ему алгоритм в широком формальном смысле, имеющий ту же запись.
Эту формулировку основного тезиса можно назвать осторожной. Она основана на убеждении, что любые мыслимые операции над конструкциями могут быть скомбинированы из натуральных операций. Предосторожность предпринята в связи с тем, что возможные исходные данные могут не быть конструкциями в смысле теории алгоритмов или с тем, что их совокупность может не быть формальным языком, а быть каким-нибудь очень «плохим» множеством (например неперечислимым). Последнее вряд ли встретится, а вот первое, пожалуй, возможно, и потому основной тезис должен быть высказан именно в осторожной форме. Второй предосторожностью, и тоже существенной, является требование, чтобы запись алгоритма в интуитивном смысле была конструкцией. Если этого не будет, может не существовать алгоритма выполнения.
Неосторожной формой основного тезиса было бы утверждение, гласящее, что всякий алгоритм в интуитивном смысле является алгоритмом в широком смысле. От подобной формулировки следует воздержаться.
В связи с широким формальным определением алгоритма приобретает интерес вопрос противоположного характера. Является ли всякий алгоритм в широком смысле также алгоритмом в интуитивном смысле? Можно предположить, что не является, потому что алгоритмы в широком смысле могут быть настолько не «прозрачны», что у человека, не проследившего этапов их построения от начала до конца, интуиция будет молчать. § 9. Коллективы алгоритмов
До сих пор мы, применяя алгоритм, рассматривали его в совокупности с одним или несколькими операндами. При этом алгоритм, если рассуждать неформально, представал перед нами в роли правила для одного исполнителя, в соответствии с которым (правилом) осуществлялась переработка одного или нескольких исходных данных. Можно говорить также, что один алгоритм перерабатывал один или несколько операндов. Но на практике нередко несколько человек выполняют общую работу. При этом возможны два случая: 1) имеется руководитель, координирующий действия исполнителей; 2) исполнители действуют согласованно и без общего руководителя. Первый случай можно свести ко второму, если руководителя посчитать некоторой разновидностью исполнителя. Описанное явление можно формализовать, рассматривая несколько алгоритмов, которые перерабатывают общий для них операнд независимо друг от друга, но результативно. Связь между алгоритмами должна проявляться при выполнении тех шагов, в результате которых может нарушаться однозначность результата.
Если до сих пор, изучая алгоритмы, мы не фиксировали внимания на порождаемых ими процессах, то теперь без этого обойтись невозможно. Поэтому уточним понятие алгоритмического процесса. При этом придется рассматривать операнд не в целом, а как состоящий из более мелких частей — элементов. Элементами операнда могут быть как отдельные буквы, так и более простые, чем операнд конструкции, заключенные в оболочки и не связанные непосредственно с частями операнда, внешними для этих оболочек. Если операнд перерабатывается первичным алгоритмом, то легко видеть, что процесс его переработки состоит из отдельных шагов, на каждом из которых выполняется некоторое преобразование (чаще всего — локальное). Можно представить себе, что на каждом шаге изменяется один или несколько элементов операнда (обозначим их совокупность через yi), причем «основанием» для такого изменения является имевшее место до начала данного шага состояние одного или нескольких элементов операнда (совокупность которых обозначим xi), а сущность этого изменения заключается в выполнении операции, указанной в соответствующем приказе первичного алгоритма (обозначим ее fi). Описанием одного шага можно считать запись
hi=(xi, fi, yi).
Процессом, порождаемым первичным алгоритмом А при заданном операнде X, назовем последовательность шагов h1, h2, ..., hn, где п — номер последнего шага, после кото-, рого операнд X окажется переработанным в соответствующий ему результат Y. При этом будем говорить, что на ни шаге происходят обращения кxi yi, причем xi читается, a yi пишется.
Заметим, что в ходе алгоритмического процесса возможно исчезновение или появление некоторых элементов операнда.
Теперь предположим, что А не является первичным алгоритмом. Тогда рассмотрим его алгоритм выполнения W0. Если W0 не является первичным, то рассмотрим его алгоритм выполнения W-1 и т. д., пока не «опустимся» до алгоритма выполнения, являющегося первичным. Из широкого формального определения алгоритма вытекает, что такой процесс закончится на некотором конечном номере k. Мы знаем, что применение А к X является не чем иным, как применением W0, к конструкции , а это все равно, что применение W-1 к и т. д., т. е. в конечном счете это все равно, что применение W-k к конструкции
. (*)
Процессом выполнения непервичного алгоритма А применительно к операнду X назовем процесс применения W-k к операнду (*). Мы видим, что если А непервичный, то для выявления порождаемого им процесса необходимо несколько расширить операнд X, присоединяя к нему символьные конструкции Wo, W-1, ..., W-k+1 с помощью связей z0, z1, ..., zk. Замечу, что вовсе необязательно «опускаться» до алгоритма выполнения, являющегося первичным. Достаточно «опуститься» до такого алгоритма выполнения, для которого тем или иным путем уже определено понятие алгоритмического процесса.
Теперь рассмотрим применение нескольких алгоритмов A1, A2, ..., An к общему для них операнду X. Те элементы операнда, к которым производятся обращения только одним из алгоритмов (или несколькими, но только по чтению), называются собственными для этого алгоритма (или каждого из этих алгоритмов), а шаги,на которых происходят такие обращения, называются регулярными. При этом подчеркнем, что обращения разных алгоритмов к одному и тому же элементу считаются происходящими всегда последовательно, хотя это следование в некоторых случаях может складываться непредсказуемым образом, а «длительность» каждого обращения считается равной нулю (это является результатом абстрагирования от времени). Если же один из алгоритмов производит запись некоторого элемента, а другие к нему обращаются (неважно, по чтению или по записи), то указанный элемент называется несобственным, а шаги, на которых к нему происходят обращения, называются критическими.
От того, в каком порядке происходят обращения к несобственному элементу, зависит окончательный результат переработки операнда. При повторных применениях группы алгоритмов к тому же общему операнду в силу неподдающихся учету обстоятельств могут получаться разные результаты. Значит, для каждого несобственного элемента должен быть зафиксирован порядок критических обращений (он зависит от решаемой задачи).
Будем считать, что в каждом частном процессе шаги занумерованы в порядке их следования (который и выражен этой нумерацией и называется естественным). Зафиксировать порядок критических обращений можно следующим образом. Каждому критическому шагу, связанному с данным несобственным элементом, будем ставить в соответствие два числа: i и ni, первое из которых является специальным номером, а второе показывает количество критических шагов, получивших одинаковые специальные номера. Для четных i будем всегда считать, что ni=1. Если первое обращение к несобственному элементу является записью, то критическому шагу, на котором оно производится, приписывают (специальный) номер 0. Если первым обращением является чтение без записи, то считают, что существует фиктивный нулевой шаг. Очередной нечетный номер приписывают каждому из шагов, на котором должно быть произведено чтение критического элемента (без записи) до новой записи. Очередной четный номер приписывают критическому шагу, который производит сперва чтение, а потом запись в тот же несобственный элемент. Если после группы чтений данного несобственного элемента такого «комбинированного» обращения нет, то процесс нумерации считают законченным. Совокупность всех критических шагов, соответствующих некоторому несобственному элементу а, для которых установлена описанным способом нумерация, называется (критическим) а-фрагментом, а порядок, заданный специальной нумерацией — специальным.
Совокупный процесс выполнения группы алгоритмов может содержать много критических фрагментов, среди которых могут встречаться и одноименные (отвечающие одному и тому же несобственному элементу). При этом критические шаги, принадлежащие одному а-фрагменту, не должны выполняться вперемежку с критическими шагами, принадлежащими другому а-фрагменту. Если началось выполнение одного а-фрагмента, то критические шаги, принадлежащие другому а-фрагменту, называются запрещенными.
Совокупный процесс переработки алгоритмами А1, А2, …, Ап операнда X называется согласованным, если каждый шаг каждого из частных процессов выполняется тогда и только тогда, когда: 1) он не является запрещенным, 2) все шаги, непосредственно ему предшествующие, в силу естественного порядка в частном процессе или в силу специальных порядков уже выполнены.
Совокупность алгоритмов А1, А2, …, Ап, при совместном применении которых к каждому операнду X, являющемуся предложением формального языка L1, возникает согласованный совокупный процесс, называется коллективом алгоритмов.
Вводя понятие коллектива алгоритмов, мы ничего не сказали, как можно обеспечить согласованность процесса их выполнения. Для различных классов алгоритмов согласованность достигается с помощью различных приемов. При этом нередко в частные процессы включаются дополнительные шаги, имеющие вспомогательное назначение.
Разъясним понятие коллектива алгоритмов на примерах.
1. Пусть алфавит языка операндов состоит из арабских цифр и русских строчных букв. Пара следующих нормальных алгоритмов составляет коллектив:
Совокупный процесс выполнения этого коллектива алгоритмов не содержит критических шагов. Исходное слово «511112 коп» приведенный коллектив алгоритмов преобразовал бы в «535 руб».
2. Более интересен коллектив следующих алгоритмов:
Этот коллектив перерабатывает исходное данное, являющееся словом, первая буква которого К, а остальные — русские строчные, в слово, образованное из русских строчных букв. Например «λ мама» он перерабатывает в «тата».
3. В дальнейших примерах будем алгоритмы записывать неформально. Предположим, что операндами являются таблицы, подобные приведенной таблице 9 (могущие от нее отличаться только первыми тремя числами, стоящими во второй строке). Четвертый столбец включен в операнд как вспомогательный. В первой строке операнда указаны имена чисел, стоящих во второй строке.
Два нижеприведенных алгоритма образуют коллектив алгоритмов.
Алгоритм 1. 1) Увеличить х на 1. Перейти к п. 2.
2) Величину х+у принять за новое значение у. Перейти к п. 3.
3) Сделать а равным 0. Перейти к п. 4.
4) Если а—\, то перейти к п. 5, иначе вернуться к п. 4.
5) Величину у—х сделать значением у. Конец.
А л г о р и т м 2. 1) Если а=0, то перейти к п. 2, иначе к п. 1.
2) Величину 2-у сделать значением г. Перейти к п. 3.
3) Сделать а равным 1. Конец.
Собственным элементом для первого алгоритма является столбец х, для второго алгоритма — столбец г, а несобственным элементом является столбец у. Вспомогательный столбец а фактически является тоже несобственным. Приказы 4-й первого алгоритма и 1-й второго алгоритма были бы недопустимы в одиночных алгоритмах, но очень полезны в алгоритмах коллектива. Описанный коллектив алгоритмов преобразует табл. 9 в табл. 10.
4. Если бы в последнем коллективе алгоритмов второй алгоритм заменить нижеприведенным, то полученная пара уже не являлась бы коллективом алгоритмов.
Алгоритм 3. 1) Если а=0, то перейти к п. 3, иначе к п. 2.
2) Увеличить г на 1. Перейти к п. 1.
3) Величину z-y сделать значением г. Перейти к п. 4.
4) Сделать а равным 1. Конец.
Для новой пары алгоритмов не существовало бы фиксированного порядка шагов для второго частного процесса. Это привело бы к тому, что если бы 1-й алгоритм совершил 3 шага за время выполнения 2-м алгоритмам 2-х шагов (при условии, что выполнение началось одновременно), то был бы один результат (табл. 11), а если бы скорость работы 1-го алгоритма была в два раза меньше (3 шага на 4 шага 2-го алгоритма), то результат был бы другой (табл. 12).
Величина а, включенная в последних двух примерах в состав операнда называется сигнальным элементом, приданным несобственному элементу у. Условия а=0 и а=1называются регулирующими предикатами.