Шаг 140.
Рекурсия на Python.
Взаимная рекурсия. Задача о станциях водоочистки. Сброс воды в каждом городе

    На этом шаге мы рассмотрим другой подход к решению этой задачи.

    Другой подход к решению задачи заключается в моделировании направлений передачи воды станциями. Существует 3 направления передачи воды, которые мы обозначим следующими символами:

В этом случае мы должны подсчитать все строки длины n, которые не начинаются с '<', не заканчиваются на '>' и не содержат подстрок '><'.

    Для создания алгоритма со взаимной рекурсией будем использовать три похожие, но разные задачи. Рассмотрим правильные строки из трёх символов, начинающихся (слева направо) с непустой подстроки и заканчивающихся подстрокой из n символов. В зависимости от начального символа, предшествующего подстроке длины n (подстрока слева от символа неуместна), существует 3 сценария (подзадачи), которые представлены схемами на рисунке 1 и заключаются в определении количества правильных подстрок в каждом из трёх случаев. Их решения обозначены функциями fv(n), f>(n) и f<(n), где нижний индекс указывает на символ, предшествующий подстроке длины n.


Рис.1. Три сценария взаимно-рекурсивного решения задачи о станциях водоочистки

    Очевидно, что размер задач - n, а их начальные условия выполняются при n = 1. В частности, fv(1) = f<(1) = 2, так как существует всего два варианта ('v' и '<') для самого последнего символа. Наоборот, f>(1) = 1, так как последняя станция очистки, получившая воду от своего соседа, должна сбросить её в реку.

    На рисунке 2 приведены декомпозиции задач.


Рис.2. Декомпозиции трех подзадач для взаимно-рекурсивного решения задачи о станциях водоочистки

    Для fv(n) и f<(n) первый символ подстроки длины n может быть любым. Поэтому общее количество правильных строк длины n равно количеству правильных строк длины n - 1 для каждого из трёх первых символов.

    Таким образом, в правой части рекурсивного правила для обеих функций будет формула fv(n - 1) + f>(n - 1) + f<(n - 1). Кроме того, поскольку их начальные условия тоже одинаковы, одинаковыми будут и сами функции. В результате мы имеем:

                     2,                                если n = 1,
    fv(n) = f<(n) =                                                     (9.7)
                     fv(n - 1) + f<(n - 1) + f>(n - 1), если n > 1    .

    Наоборот, для f>(n) символ '<' не может быть первым в строке длины n. Поэтому в правой части её рекурсивного правила будет fv(n - 1) + f>(n - 1). А сама функция вместе с ее начальным условием будет такой:

            1,                    если n = 1,
    f>(n) =                                              (9.8)
            fv(n - 1) + f>(n - 1), если n > 1    .

    Итак, решение f(n) исходной задачи можно выразить двояко:

    f(n) = fv(n - 1) + f>(n - 1) = f>(n).

    Первое равенство говорит о том, что первым символом строки может быть или 'v', или '<', и в этом случае мы должны сложить число правильных строк длины n - 1, начинающихся с этого символа. Второе равенство следует из рекурсивного правила для f>(n). Наконец, можно показать, что fv(n) = f<(n) = F(2n + 1), а f>(n) = F(2n), где F - функция Фибоначчи.

    На следующем шаге мы рассмотрим циклические ханойские башни.




Предыдущий шаг Содержание Следующий шаг