Ресурси для покращення вашого розуміння рекурсії? [зачинено]


13

Я знаю, що таке рекурсія (коли паттен знову виникає всередині себе, як правило, це функція, яка викликає себе по одній із своїх ліній після умовного прориву ... правда?), І я можу зрозуміти рекурсивні функції, якщо уважно їх вивчити. Моя проблема полягає в тому, що коли я бачу нові приклади, я спочатку завжди плутаюся. Якщо я бачу цикл або відображення, зіштовхування, гніздування, поліморфні дзвінки тощо, я знаю, що відбувається лише дивлячись на нього. Коли я бачу рекурсивний код, мій процес думки, як правило, "wtf це?" після чого "о, це рекурсивно", а потім "я думаю, що це має спрацювати, якщо вони кажуть, що це так".

То чи маєте ви поради / плани / ресурси для формування навичок у цій галузі? Рекурсія - це якесь дивне поняття, тому я думаю, що спосіб її вирішення може бути однаково дивним і невидимим.


28
Щоб зрозуміти рекурсію, потрібно спочатку зрозуміти рекурсію.
Андреас Йоханссон

1
Доктор Сеусс "Кіт у капелюсі повертається", це може бути не зовсім корисним, але рекурсивний виклик кішки позбавляється від цієї примхливої ​​плями. :-) Це також має перевагу дуже швидкого читання!
DKnight

2
Практика, практика, практика.
Девід Торнлі

20
Вже відповіли на це запитання: programmers.stackexchange.com/questions/57243/…
Грем Борланд

3
@Graham Borland: Це приклад нескінченної рекурсії. У більшості програм відсутність базового випадку зазвичай призводить до переповнення стека або помилки поза пам'яттю. Для користувачів веб-сайту це може просто призвести до плутанини. ;)
FrustratedWithFormsDesigner

Відповіді:


10

Почніть з чогось простого і простежте це олівцем і папером. Серйозно. Хорошим місцем для початку є алгоритми обходу дерев, оскільки з ними набагато легше обробляти за допомогою рекурсії, ніж звичайну ітерацію. Це не повинно бути складним прикладом, але щось просто і з цим можна працювати.

Так, це дивно, а іноді і контр-інтуїтивно, але як тільки він клацає, колись ви говорите "Еврика!" вам буде цікаво, як ви цього раніше не розуміли! ;) Я запропонував дерева, тому що вони (ІМО) - це найпростіша структура, яку можна зрозуміти в рекурсії, і з ними легко працювати, використовуючи олівець і папір. ;)


1
+1 - це те, як я це зробив. наприклад, якщо ви використовуєте OO, складіть кілька класів із стосунками батьків-дитини, а потім спробуйте створити функцію / метод, який перевіряє, чи є об'єкт у конкретного предка.
Альб

5

Я настійно рекомендую схему, використовуючи книгу «Маленька Лиспер». Після того, як ви закінчите з цим, ви будете розуміти рекурсию, глибоко вниз. Майже гарантовано.


1
+1 Ця книга справді зробила це для мене. Але він був перейменований на "Маленький
Схемер

4

Я точно пропоную SICP. Також тут слід ознайомитись із вступними відео про авторські курси тут ; вони неймовірно відкривають розум.

Ще одна дорога, не настільки суворо пов'язана з програмуванням, - це читання Геделя, Ешера, Баха: Вічна золота коса Хофстадтера. Як тільки ви пройдете це, рекурсія буде виглядати так само природно, як арифметика. Крім того, ви переконаєтесь, що P = nP, і ви хочете створити мислячі машини - але це побічний ефект, який настільки малий у порівнянні з перевагами.


GEB варто прочитати все одно; навіть якщо деякі речі, про які він розповідає, трохи датуються (деякий прогрес у фундаментальних дослідженнях CS був досягнутий за останні 40 років), основне розуміння не є.
Стипендіати Дональ

2

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

Наприклад, швидкий сорт працює в тому, що він переміщує елемент у списку на дві половини, а потім знову застосовується до кожної з цих половинок. Коли відбувається початкове сортування, його не турбує питання про сортування двох половинок у цій точці. Скоріше взяти шарнірний елемент і покласти всі елементи менші за цей елемент з одного боку, а всі елементи більші або рівні з іншого боку. Чи має сенс це, як він може рекурсивно називати себе в цей момент для сортування двох нових менших списків? Вони теж є списками. Просто менше. Але їх все одно потрібно сортувати.

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

EDIT: Я також мушу додати, що ключовим елементом рекурсії є гарантована здатність функції бути в змозі зупинити. Це означає, що зрив вихідної проблеми повинен постійно зменшуватися, і, зрештою, повинен бути гарантований момент зупинки (точка, в якій нова підзадача або вирішена, або вже вирішена).


Так, я думаю, я бачив швидке пояснення сортування раніше, я можу передбачити, як це працює з вашого нагадування вище. Наскільки експресивна / гнучка рекурсія - чи можна зв'язати більшість проблем з рекурсивним підходом (навіть якщо це не оптимально)? Я бачив, як люди відповідають у кодуванні головоломок у мережі, що більшість людей вирішуються процедурно, наче вони можуть використовувати рекурсію, коли захочуть просто для чорта. Я також читав один раз, я думаю, що деякі мови залежать або повторюються, щоб замінити контурну конструкцію. І ви згадуєте про гарантовану зупинку. Я відчуваю, що одна з цих речей може бути ключовою.
Андрій М

Хорошою простою початковою проблемою, яку ви могли б створити самостійно, було б написати рекурсивну програму, яка знайде факторіал числа.
Кеннет

Будь-яка циклічна структура може бути введена в рекурсивну структуру. Будь-яка рекурсивна структура може бути поміщена в циклічну структуру ... більш-менш. Потрібен час і практика, щоб можна було дізнатися, коли і коли не використовувати рекурсію, тому що ви повинні пам’ятати, що при використанні рекурсії існує велика кількість витрат на ресурси, що використовуються на апаратному рівні.
Кеннет

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

тож ось моя спроба на фактор. Чесно кажучи, я це бачив і раніше, і хоча я писав це з нуля, а не пам’яті, це, мабуть, все ж простіше, ніж це було б. Спробував це в JS, але був помилка розбору, але працює в Python def factorial(number): """return factorial of number""" if number == 0: return 0 elif number == 1: return 1 else: return number * factorial(number - 1)
Ендрю М

2

Особисто я вважаю, що найкраща ставка - це практика.

Я дізнався рекурсію з LOGO. Ви можете використовувати LISP. Рекурсія в цих мовах природна. В іншому випадку ви можете порівняти це з вивченням математичних наборів і рядів, де ви виражаєте, що наступне, виходячи з того, що було раніше, тобто u (n + 1) = f (u (n)), або більш складних рядів, де у вас є кілька змінних і множинні залежності, наприклад, u (n) = g (u (n-1), u (n-2), v (n), v (n-1)); v (n) = h (u (n-1), u (n-2), v (n), v (n-1)) ...

Тож моя пропозиція полягатиме в тому, щоб ви знайшли прості (за їх виразом) стандартні "проблеми" рекурсії та спробували їх реалізувати на своїй обраній мові. Практика допоможе вам навчитися думати, читати та висловлювати ті "проблеми". Зауважте, що часто деякі з цих проблем можна виразити за допомогою ітерації, але рекурсія може бути більш елегантним способом їх вирішення. Одним із таких є розрахунок факторних чисел.

Графічні "проблеми", які я вважаю, полегшують їх бачення. Тож знайдіть пластівці Коха, Фібоначчі, криву дракона та фрактали взагалі. Але також подивіться на алгоритм швидкого сортування ...

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


2

Структура та інтерпретація комп'ютерних програм

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


0

Наскільки мені подобаються SICP та Gödel, Escher, Bach: Вічна золота коса , LISP Touretzky : Ніжне введення в символічні обчислення також добре справляється з введенням рекурсії.

Основна концепція така: По-перше, ви повинні знати, коли ваша рекурсивна функція закінчена, щоб вона могла повернути результат. Тоді ви повинні знати, як взяти незакінчену справу і звести її до чогось, що ви можете повторити. Для традиційного факторіального (N) прикладу ви закінчите, коли N <= 1, а незавершений випадок - N * факторіал (N-1).

Для набагато потворнішого прикладу є функція Ackermann A (m, n).

A(0,n) = n+1.                                   This is the terminal case.
A(m,0) = A(m-1,1) if m > 0.                     This is a simple recursion.
A(m,n) = A(m-1, A(m, n-1)) if m > 0 and n > 0.  This one is ugly.

0

Я пропоную пограти з деякими функціональними мовами стилю ML, такими як OCaml або Haskell. Я виявив, що синтаксис відповідності шаблону дійсно допоміг мені зрозуміти навіть відносно складні рекурсивні функції, безумовно, набагато краще, ніж схеми ifта condзаяви. (Я вивчив Haskell і схему одночасно.)

Ось банальний приклад контрасту:

(define (fib n)
   (cond [(= n 0) 0]
         [(= n 1) 1]
         [else (+ (fib (- n 1)) (fib (- n 2)))]))

і з узгодженням шаблону:

fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

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

Я особливо рекомендую Haskell, оскільки це проста мова з дуже приємним синтаксисом. Це також значно полегшує роботу з більш досконалими ідеями, такими як corecursion :

fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
fib n = fibs !! n

(Ви не зрозумієте вищевказаний код, поки трохи не пограєте з Haskell, але будьте впевнені, що це в основному магічно: P.) Впевнені, що ви можете зробити те ж саме з потоками в Scheme, але це набагато природніше в Haskell.


0

Це не надруковано, але якщо ви зможете знайти його, "Рекурсивні алгоритми" Річарда Лоренца - це не що інше, як рекурсія. Він охоплює основи рекурсії, а також конкретні рекурсивні алгоритми.

Приклади є у Паскалі, але не настільки великі, що вибір мови набридає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.