Эти две функции симметричны по a и b, и поэтому точно так же симметрична f. При анализе программы мы ограничены действиями, происходящими внутри цикла. Величины r и s являются вспомогательными переменными, которые не оставляют никакой проблемы. Трудность вызывают преобразования, проделываемые над p и q. Чтобы ясно увидеть эту трудность, осуществим введение новых переменных без разрушения старых. Перепишем наш цикл:
ПОКА q >= eps ВЫПОЛНЯТЬ
r := (q/p)2; s := r/(r + 4)
p' := (2 * s + 1) * p; q' := s * q
p := p'; q := q'
ВЕРНУТЬСЯ
Рассмотрим действия этой программы, производимые над данными a, b с одной стороны и над ac, bc с другой.
Когда мы входим в цикл, то и p, и q умножаются на с при переходе от первого вычисления ко второму.
Это не меняет величины r и, следовательно, не меняет величины s. Таким образом, p и q в этих вычислениях умножаются на одни и те же сомножители, так что значения p', q' во втором вычислении получаются из значений p, q в первом вычислении умножением их обоих на c. Следовательно, мы еще раз входим в цикл при том же соотношении между входными данными; следовательно, это соотношение будет иметь место при каждом входе в цикл, и, следовательно, также и на выходе из цикла. Отсюда получаем, что f(ac, bc) = cf(a, b).
Выполнение программы для вычисления g(x) = f(x, 1) с x = 1 и eps = 10-5 дает мне результат, равный 1.4142.
Дальше считать бесполезно, это 2.
Я немедленно изменяю программу, чтобы она выполняла вывод не только величины g, но также и g2. Я получаю:
x g2(x)
1 2
2 5
3 10
4 17
Нет возможности сомневаться: g(х) = х2 + 1.
Перенося эту формулу в соотношение между f и g, мы видим, проделав все вычисления, что
f (a, b) = a2 + b2.
«Осталось только» доказать это. Мы не можем доверять заверениям программистов, утверждающих, что их программа делает то-то и то-то. При входе в цикл p и q имеют значения а и b в каком-то порядке, поэтому
p2 + q2 = a2 + b2.
Что происходит с величиной p2 + q2 после изменений, которым p и q подвергаются в цикле? Вычислим p'2 + q'2:
p'2 + q'2 = (2s + 1)2p2 + s2q2 = s2 (4р2 + q2) + 4sp + р2.
Вычислим s:
r := q2/p2, s = r/(r + 4) = q2(q2 + 4p2),
откуда, наконец,
s (4р2 + q2) = q2.
Возвращаясь отсюда к предыдущему соотношению, получаем
p'2 + q'2 = sq2 + 4sp2 + р2 = s(4р2 + q2) + p2 = p2 + q2.
Таким образом, все кончено… Это соотношение гарантирует, что p2 + q2 является инвариантом цикла. При каждом входе в цикл выполняется соотношение
p2 + q2 = a2 + b2.
При выходе из цикла
p2 + q2 = a2 + b2, причем q ерs.
Отсюда следует, что
p2 = (a2 + b2) * (1 - q2/(a2 + b2)).
Cpaey получаем, что
p = a2 + b2
с относительной ошибкой eps2/(2 * (a2 + b2)).
Чтобы получить точность до 10-5, совершенно ненужно брать eps = 10-5; более чем достаточно eps = 0.004. Эта программа сходится очень быстро.
3. Игры без стратегии
Игра 13.
Задача о наиболее длинном взятии не имеет однозначного решения. Вот как ее сделал я — с учетом моих привычек в программировании и упрощений, предоставляемых моим микрокомпьютером.
Я представил всю игру одной-единственной цепочкой символов с кодами возврата каретки, расположенными надлежащим образом — так, чтобы визуализация игры сводилась к визуализации этой цепочки бее какого-либо дополнительного исследования. Куры обозначаются в этой цепочке присвоенными им буквами, лисы — буквами X, пустые игровые поля обозначаются точками. Остальные символы (пробелы или возврат каретки) не отвечают никаким используемым игровым полям. Я добавил в начале и в конце по строчке пробелов, чтобы не было необходимости изучать возможность некоторых перемещений на границе игрового поля.
Я не храню положений лис с помощью двух переменных. Я отыскиваю их положение в цепочке, представляющей игру, с помощью функции «положение» языка LSE. Это — существенная деталь. Поиск наиболее длинного взятия я осуществляю итеративно. Я образую две цепочки:
— одна из них содержит список кур, уже взятых при исследовании данного пути (это — последовательность букв взятых кур),
— вторая цепочка содержит дуплеты: положение в игре и рассматриваемое направление (мы осуществляем взятие, исходя из положения x и двигаясь в направлении, обозначенном через i).
Находясь в положении x и в направлении i я смотрю, есть ли кура на поле x + d[i]. Если ее нет, то в этом направлении никакое взятие невозможно. Если же такая кура есть, то я смотрю, не содержится ли буква этой куры в цепочке уже взятых кур. Если содержится, то в этом направлении ничего не сделаешь. Если же эта кура еще не взята, то я проверяю, действительно ли поле x + 2 * d[i] содержит именно точку — в противном случае никакого взятия нет. Действуя таким образом, я не сталкиваюсь ни с какими трудностями на краях (там есть предохранительная строка, и она не содержит ни одной куры).