Які міркування можна визначити, чи можна використовувати рекурсію для вирішення проблеми?


10

Іноді в інтерв'ю я можу використовувати рекурсію для вирішення проблеми (наприклад, додавання 1до нескінченного цілого числа) або коли проблема виявляється придатною для використання рекурсії. Іноді це може бути просто завдяки використанню рекурсії для вирішення проблеми, тому, не задумуючись, для вирішення проблеми використовується рекурсія.

Однак які міркування перш ніж ви зможете вирішити, чи підходить використовувати рекурсію для вирішення проблеми?


Деякі думки у мене були:

Якщо ми використовуємо рекурсію на даних, які щоразу зменшуються вдвічі, здається, що це не проблема з використанням рекурсії, оскільки всі дані, які можуть вміститися в 16 ГБ оперативної пам’яті, або навіть на 8 ТБ жорсткий диск, можуть оброблятися рекурсією просто на 42 рівні глибиною. (так що жодного переповнення стека (я думаю, в деяких умовах стек може бути глибоким на рівні 4000, набагато більше 42, але в той же час це також залежить від того, скільки локальних змінних у вас, як кожен стек викликів, займають більше пам'яті) якщо є багато локальних змінних, і переповнення стека визначає саме розмір пам'яті, а не рівень.

Якщо ви обчислюєте числа Фібоначчі за допомогою чистої рекурсії, вам дійсно доведеться турбуватися про складність часу, якщо ви не кешуєте проміжні результати.

А як щодо додавання 1до нескінченного цілого числа? Можливо, це дискусійно, адже, чи будете ви працювати з числами, що мають 3000 цифр або 4000 цифр завдовжки, настільки великі, що це може спричинити переповнення стека? Я не думав про це, але, можливо, відповідь «ні», ми не повинні використовувати рекурсію, а просто використовувати просту петлю, бо що робити, якщо в якомусь додатку кількість дійсно повинна бути 4000 цифр, щоб перевірити, чи є властивості числа, наприклад, чи є простим число чи ні.

Остаточне запитання: які міркування, перш ніж ви зможете вирішити використовувати рекурсію для вирішення проблеми?


7
Насправді це досить просто: "Чи рішення тривіальне, якщо я можу припустити, що рішення трохи меншої проблеми відоме?"
Кіліан Фот

а як щодо числа Фібоначчі або додавання 1до нескінченного цілого числа? Ви можете сказати, так, вони зводяться до менших проблем, але чиста рекурсія для цього не підходить
неополярність

Це може бути вам корисним - stackoverflow.com/questions/3021/…
Кішор Кундан

Відповіді:


15

Одне врахування полягає в тому, чи призначений ваш алгоритм абстрактним рішенням чи практичним виконаним рішенням. У першому випадку атрибути, які ви шукаєте, - це правильність та простота розуміння для вашої цільової аудиторії 1 . В останньому випадку продуктивність також є проблемою. Ці міркування можуть вплинути на ваш вибір.

Другий розгляд (для практичного рішення) полягає в тому, чи застосовується мова програмування (або, більш чітко, її реалізація), яку ви використовуєте? Без усунення хвостового виклику рекурсія проходить повільніше, ніж ітерація, і глибока рекурсія може призвести до проблем переповнення стека.

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

Нарешті, іноді вибір між рекурсивними та нерекурсивними рецептурами мотивується необхідністю довести (у формальному сенсі) властивості щодо алгоритму. Рекурсивні рецептури прямо прямо дозволяють довести індукцію.


1 - Це включає такі міркування, як, чи цільова аудиторія ... а це може включати програмістів, які читають практичний код ... бачила б один стиль рішення як "більш природний", ніж інший. Поняття "природне" буде змінюватися від людини до людини, залежно від того, як вони засвоїли програмування або алгоритміку. (Я кидаю виклик кожному, хто пропонує "природність" як основний критерій для вирішення питання про використання рекурсії (або ні) для визначення "природності" в об'єктивному вираженні; тобто як би ви це виміряли.)


2
Деякі проблеми просто більш природно виражені за допомогою рекурсії. Обхід дерева, наприклад.
Френк Хілеман

Оновлено мою відповідь на вирішення цього питання,
Stephen C

1
Що стосується "природності", наприклад, обхід дерев без рекурсії, наприклад, має тенденцію створювати більший, менш загальний код призначення. Розглянемо, наприклад, використання поліморфних викликів для обходу дерева, з різною поведінкою для листяних та композитних вузлів. Це неможливо без рекурсії.
Френк Хілеман

1) Ви бралися за мій виклик, щоб визначити "природне" ще? 2) Оскільки можна моделювати рекурсію, використовуючи структуру даних стека, можна також реалізувати обхід дерева таким чином. Це може бути не найефективнішим способом ... і він не дасть вам найчитабельнішого коду ... але це, безумовно, можливо, і практично це зробити.
Стівен C

До речі, перша мова програмування, яку я вивчив (FORTRAN 4), зовсім не підтримувала рекурсію.
Стівен C

1

Як програміст на C / C ++, моя головна увага - це продуктивність. Мій процес прийняття рішення виглядає приблизно так:

  1. Яка максимальна глибина стека викликів? Якщо занадто глибоко, позбудьтесь від рекурсії. Якщо дрібно, перейдіть до 2.

  2. Чи може ця функція бути вузьким місцем моєї програми? Якщо так, перейдіть до 3. Якщо ні, продовжуйте рекурсію. Якщо ви не впевнені, запустіть профілер.

  3. Яка частка часу процесора витрачається на рекурсивні виклики функцій? Якщо виклики функцій займають значно менше часу, ніж решта функціонального тіла, нормально використовувати рекурсію.


0

Однак які міркування перш ніж ви зможете вирішити, чи підходить використовувати рекурсію для вирішення проблеми?

Коли пишуть функції в Scheme, я вважаю природним писати рекурсивні функції хвоста, не надто замислюючись.

Коли я пишу функції на C ++, я відчуваю дискусію перед тим, як використовувати рекурсивну функцію. Питання, які я собі задаю:

  • Чи можна обчислити за допомогою ітеративного алгоритму? Якщо так, використовуйте ітеративний підхід.

  • Чи може глибина рекурсії зростати за розмірами моделі? Нещодавно я натрапив на випадок, коли глибина рекурсії зросла майже до 13000 завдяки розмірам моделі. Мені довелося перетворити функцію, щоб використовувати ітеративний алгоритм після поспіху.

    З цієї причини я б не рекомендував писати алгоритм переходу дерева з використанням рекурсивних функцій. Ви ніколи не знаєте, коли дерево стане занадто глибоким для вашого середовища.

  • Чи може функція стає занадто згорнутою за допомогою ітеративного алгоритму? Якщо так, використовуйте рекурсивну функцію. Я не намагався писати qsortза допомогою ітеративного підходу, але у мене є відчуття, що використання рекурсивної функції є більш природним для цього.


0

Для чисел Фібоначчі наївна "рекурсія" просто абсолютно дурна. Це тому, що це призводить до того, що та сама підпроблема вирішується знову і знову.

Насправді існує тривіальна варіація чисел Фібоначчі, де рекурсія є дуже ефективною: Враховуючи число n ≥ 1, обчисліть як fib (n), так і fib (n-1). Отже, вам потрібна функція, яка повертає два результати, дозволить викликати цю функцію fib2.

Реалізація досить проста:

function fib2 (n) -> (fibn, fibnm1) {
    if n ≤ 1 { return (1, 1) }
    let (fibn, fibnm1) = fib2 (n-1)
    return (fibn + fibnm1, fibn)
}

ви вважаєте, що можете написати програму загальною мовою? і ваш fib2повертає пару чисел, і ваш fib2()не відповідає інтерфейсу fib(), який, даючи номер, повертає число. Здається, ви fib(n)повинні повернутися, fib2(n)[0]але будьте конкретні
неополярність
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.