Рекурсия - как мы все знаем - это одна из тех проблем, когда мыслят вокруг, как будто достигают «вехи» в вашем плавании.
Но когда дело доходит до фактического использования его в реальных задачах - знания механизма рекурсии недостаточно, нужно также понимать природу проблем, для которых рекурсия является наиболее подходящим решением.
Итак, мой вопрос заключается в следующем ...
- Каковы "шаблоны проблемы", которые требуют решения рекурсии
- является ли рекурсия формой стратегии «разделяй и властвуй» или формой «повторного использования кода», или же сама является шаблоном проектирования
- Можете ли вы привести пример реальной проблемы, когда рекурсия приходит на ум как немедленное решение?
-- ОБНОВИТЬ --
многие ответы ссылаются на «реальные проблемы» как обход дерева, факториал и т. д. Я бы предпочел «РЕАЛЬНЫЕ реальные проблемы» - позвольте мне привести вам пример ...
У нас был БОЛЬШОЙ кусок текста (около 30 МБ текста в виде связанного списка structs
), и нам нужно было создать его индекс для полнотекстового поиска. Нам нужно было сохранить весь индекс в памяти и переиндексировать текст каждые 10 минут.
Каждые 10 минут мы сравнивали весь текст (два связанных списка, строка за строкой) с вновь созданным фрагментом текста, чтобы увидеть, какая строка была изменена, и затем мы переиндексируем только эту строку - таким образом. мы могли бы избежать необходимости переиндексировать ВЕСЬ текст. Помните - нам нужно было найти точки различия между двумя связанными списками по 30 МБ.
Один из моих коллег придумал фантастическую программу, которая использовала HEAVY-рекурсию для сравнения линий - и затем собирала позиции, в которых патроны различались в массиве - да, я знаю, это звучит странно - как рекурсия может помочь здесь - но это сделал.
Дело в том, как он мог понять, что эту проблему можно решить с помощью интенсивного использования рекурсии?
Ответы:
Я бы не сказал, что существует такая вещь, как шаблон проблемы для использования рекурсии. Каждая функция, которая может быть реализована с помощью рекурсии, также может быть реализована итеративно, часто путем нажатия и выталкивания стека.
Это вопрос выражения, а также производительности. Итерационные алгоритмы часто имеют лучшую производительность и их легче оптимизировать. Однако рекурсивные алгоритмы выигрывают от более ясного выражения и, следовательно, часто их легче читать, понимать и реализовывать.
Некоторые вещи даже не могут быть выражены без рекурсии, например, бесконечные списки. Так называемые функциональные языки в значительной степени полагаются на рекурсию, поскольку это их естественный способ выражения. Поговорка: «Рекурсивное программирование - это функциональное программирование, выполненное правильно».
Я бы не назвал это шаблоном дизайна. Это вопрос выражения. Иногда рекурсивное выражение просто более мощное и более выразительное и, следовательно, приводит к лучшему и более чистому коду.
Все, что нужно для обхода деревьев, будет правильно выражено рекурсивным алгоритмом.
источник
Ни. Разделяй и властвуй использует рекурсию. Но рекурсия не обязательно разделяет и завоевывает, поскольку последняя означает разделение проблемы на две (или более) части и решение каждой из них симметрично. В рекурсии вы этого не делаете.
Повторное использование кода совершенно не связано, и шаблон проектирования вступает в игру на гораздо более высоком уровне. Например, даже разделяй и властвуй (также паттерн более высокого уровня, чем рекурсия) все еще не считается паттерном проектирования - скорее это алгоритмический паттерн.
Обход дерева. Или, в более общем смысле, обход графа. Это, в частности, включает в себя обход структуры папок.
И, конечно, все, что использует метод «разделяй и властвуй» или динамическое программирование, поскольку оба они естественным образом выражаются в терминах рекурсии.
источник
Происходя из самоподобия фракталов, я бы сказал, что само-равенство или самоидентификация (или как бы она ни называлась) является типичным образцом проблемы рекурсии. Т.е. проблему можно разделить на подзадачи, которые имеют ту же структуру, что и основная проблема.
В упомянутом обходе дерева каждое поддерево само по себе является полным деревом, так же как и главное дерево, и главное дерево может быть поддеревом в другом дереве.
Поэтому я предполагаю, что ваш коллега обнаружил свойства самоуравнения вашей проблемы с индексацией. Или он пошел другим путем и превратил проблему в самоуравненное представление.
источник
Что ж, рекурсию можно легко понять, если попытаться преобразовать императивные циклы в функциональные функции. В любом случае, давайте попробуем дать ответы на все вопросы:
Если у вас есть древовидная структура или алгоритм, вам понадобится рекурсия. Если ваш императивный код имеет дело со стеком, вам понадобится рекурсия. Если определенный шаг в вашем алгоритме зависит от предыдущих шагов (думаю, циклы), вам нужна рекурсия. Нужно здесь интерпретировать как можно использовать.
Никто. Разделяй и властвуй использует рекурсию, но может быть реализовано с помощью стеков. Повторное использование кода относится к чему-то еще. Шаблоны проектирования сложнее, чем простая рекурсия.
Разбор и все, что имеет дело с древовидными структурами. Даже неявные древовидные структуры.
источник
Если есть способ упростить его, чтобы он был легким, это может быть ключом к тому, что рекурсия может сработать. Вы можете воспользоваться сортировкой и поиском примеров, где существуют рекурсивные решения, такие как сортировка слиянием и бинарный поиск соответственно.
Следует иметь в виду, что некоторые проблемы могут быть плохо решены с помощью рекурсии, как факториал.
Что касается примера из реальной жизни, в котором я бы использовал рекурсию, то поиск книги с книжной полки можно сделать довольно легко рекурсивным способом. Я просто смотрю на книгу, и если это не то, чего я хочу, я перехожу к следующей. Я останавливаюсь, когда найду книгу или попадаю в конец ряда. Цикл проверки книги и перехода к следующей может быть выполнен рекурсивно. Возможно, это слишком реальный пример.
источник
В общих чертах рекурсия требуется, когда вы решаете задачу, где f (x) = f (g (x)) . Если вы не в порядке с бесконечной рекурсией, g (x) не должно оцениваться как x .
Ни один из вышеперечисленных. Это просто способ сделать то же самое несколько раз, иногда в зависимости от изменений на входе. В этом отношении концепция была намного дольше, чем шаблоны проектирования, повторное использование кода или даже компьютеры.
Факториалы, последовательность Фибоначчи, обход деревьев и многие другие проблемы могут быть решены с помощью recusrion. Рекурсия в смысле вызова функции сама по себе не обязательно является наилучшим способом реализации такого рода вещей; Существуют и другие способы достижения того же эффекта (например, стек и цикл), который может быть более желательным.
источник
Когда вы пишете рекурсивный алгоритм, вы обычно переводите рекурсивное определение проблемы в код. Тогда вам даже не нужно знать, как это будет выполнено.
Это то, что происходит в функциональном программировании. На самом деле вы указываете, что (определение), а не как . Другими словами, вы определяете основание, а затем определяете свою проблему в терминах подзадачи.
Например рассмотрим
Factorial
алгоритмЗатем, когда вы сталкиваетесь с проблемой, вы должны подумать, можете ли вы определить ее рекурсивно или нет, если вы можете определить ее рекурсивно, вы почти решили ее.
Однако любая рекурсивная функция не должна быть рекурсивным определением. Вы можете определить базу и связать (определить) решение основной проблемы с решением (определением) подзадач. Но для этого отношения вам может понадобиться процедура.
Пример ,
MergeSort
в которомmerge
может быть необходимо , чтобы процедура связать определение или решение сортировки всего массива рода суб-массивы.источник