Upload
david-academy
View
181
Download
8
Embed Size (px)
DESCRIPTION
Училищен курс по програмиране на C# (2013/2014) Занятие №5: Рекурсия
Citation preview
2013Copyright © 2013 DAVID Holding Company
Курс по програмиране на C#
Занятие №5Рекурсия
Copyright © 2013 DAVID Holding Company
Съдържание 1/2
• Рекурсия– Какво е рекурсия?– Как работи рекурсията?
• Видове рекурсия– Единична и множествена рекурсия– Пряка и косвена рекурсия– Опашна рекурсия
• Използване на рекурсия– Приложение– Основни принципи– Предимства и недостатъци– Оптимизация
Copyright © 2013 DAVID Holding Company
Съдържание 2/2
• Примери за рекурсия– Факториел– Числа на Фибоначи– Обръщане на масив– Двоично търсене в сортиран масив– Търсене на път в лабиринт
Copyright © 2013 DAVID Holding Company
Рекурсия
• Какво е „рекурсия“?– За определение на „рекурсия“ вж. „Какво е рекурсия“?– Рекурсия в най-общ смисъл е представяне на един обект или
процес чрез самия него– Рекурсия в математиката е изразяване на една функция чрез
самата нея– Рекурсия в компютърното програмиране е решение, при което
едно функция използва сама себе си
Copyright © 2013 DAVID Holding Company
Рекурсия
• Как работи извикването на функция?– При извикване на функция, в стека се запазва информация за
локалните променливи и мястото на извикването и параметрите– При връщане от извикване на функция, изпълнението
продължава от мястото на извикването, като локалните променливи и параметрите се възстановяват
• Как работи рекурсията?– Описаната схема на извикване на функции работи при
произволен брой вложени извиквания на различни функции– В частност тези извиквания могат да бъдат рекурсивни– Внимание: При прекалено много вложени извиквания, стекът
може да се препълни това да доведе до грешка и прекратяване изпълнението на програмата
Copyright © 2013 DAVID Holding Company
Рекурсия
1. Директор към секретарката:– „Заминаваме на командировка в чужбина за седмица.“
2. Секретарката към мъжа си:– „Заминавам с шефа на командировка в чужбина за седмица.“
3. Мъжът към любовницата си:– „Жена ми заминава в командировка. Тази седмица сме заедно.“
4. Любовницата (учителка) към ученика си:– „Болна съм, тази седмица. Няма да имаме уроци.“
5. Ученикът към дядо си:– „Дядо, тази седмица идвам нагости, няма да имам уроци.“
6. Дядото (директорът) към секретарката си:– „Внукът идва нагости тази седмица. Командировката се отлага.“
7. Go to 1.
Copyright © 2013 DAVID Holding Company
Видове рекурсия
• Единична и множествена рекурсия– Единична рекурсия е тази, при която има само едно
рекурсивно извикване– Множествената рекурсия е тази, при която има повече от
едно рекурсивно извикване
• Пряка и косвена рекурсия– Пряка рекурсия е тази, при която функцията извиква себе си
директно– Косвена (непряка) рекурсия е тази, при която функцията
извиква себе си чрез извикване на друга функция– Косвената рекурсия се нарича още споделена рекурсия, тъй
като всички функции участващи в нея се извикват рекурсивно, макар и косвено
Copyright © 2013 DAVID Holding Company
Видове рекурсия
• Опашна рекурсия– Опашно извикване е извикване на функция като последно
действие в друга функция– Извикваната функция може да връща резултат, който да се
върне на извикващата функция– Ако в резултат на опашното извикване в дадена функция
същата функция бъде извикана отново, то говорим за опашна рекурсия
– С други думи, опашна рекурсия е тази, при която рекурсивното извикване се осъществява в „опашката“ на функцията
– Опашната рекурсия винаги може да бъде сведена до итерация (оптимизация на опашна рекурсия)
Copyright © 2013 DAVID Holding Company
Използване на рекурсия
• Приложение– Решаване на математически задачи, решенията на които са
зададени с рекурсивна връзка– Разбиване на решението на по-малки сходни решения и
обединяване на техните резултати („разделяй и владей“) – динамично програмиране/оптимиране.
– Рекурсия + таблици за търсене = мемоизация
• Основни принципи– Всяко рекурсивно извикване трябва да опростява решението– Рекурсията трябва да съдържа условие за край (т.нар. дъно на
рекурсията)
Copyright © 2013 DAVID Holding Company
Използване на рекурсия
• Предимства– Опростява решението на множество задачите чрез разбиването
им на малки прости подзадачи
• Недостатъци– Значително по-бавна работа, поради засилено използване на
стека– Прекомерно използване на стек
• Оптимизация– Свеждане на рекурсията до итерация, когато това е възможно
• Използване на собствени стекови структури за симулиране на рекурсия
– Използване на мемоизация, когато рекурсивните извиквания дават еднозначен резултат при едни и същи аргументи
Copyright © 2013 DAVID Holding Company
Примери за рекурсия – факториел
• Какво е факториел?– Класическа дефиниция
n! = 1. 2 . 3 . … . (n-1) . n , n ∈ ℕ0! = 1 (граничен случай)
– Рекурсивна дефиницияn! = n . (n-1)! , n ∈ ℕ0! = 1 (дъно на рекурсията)
• Факториелът е бързо растяща функция• За големи стойности, използвайте
BigInteger вместо int
Copyright © 2013 DAVID Holding Company
Примери за рекурсия – факториел
// факториел - итеративноint GetFactorialIter(int n){ int result = 1; // n! = 1.2.3. … .n for (int i = 1; i <= n; i++) result *= i; return result;}
• Итеративно намиране на факториел– Итеративна дефиниция:
n! = 1. 2 . 3 . … . (n-1) . n , n ∈ ℕ0! = 1 (граничен случай)
Copyright © 2013 DAVID Holding Company
Примери за рекурсия – факториел
// факториел – рекурсивноint GetFactorialRec(int n){ // 0! = 1 if (n <= 0) return 1; // n! = n . (n-1)! return n * GetFactorialRec(n-1);}
• Рекурсивно намиране на факториел– Рекурсивна дефиниция:
n! = n . (n-1)! , n ∈ ℕ0! = 1 (дъно на рекурсията)
Copyright © 2013 DAVID Holding Company
Примери за рекурсия – числа на Фибоначи
// числа на Фибоначи – рекурсивноint GetFibonacciNumbersRec(int n){ // F0 = 1, F1 = 1 if (n <= 1) return 1; // Fn = Fn-1 + Fn-2
return GetFibonacciNumbersRec(n - 1) + GetFibonacciNumbersRec(n - 2);}
• Кои са числата на Фибоначи?– Рекурсивна дефиниция:
Fn = Fn-1 + Fn-2
F0 = 1, F1 = 1
Copyright © 2013 DAVID Holding Company
Примери за рекурсия – обръщане на масив
// обръщане на масив – рекурсивноvoid ReverseArrayRec(int[] a, int start, int end){ // краищата на масива са се разминали if (end <= start) return; // разменяме крайните елементи int t = a[end]; a[end] = a[start]; a[start] = t; // обръщаме подмасива ReverseArrayRec(a, start + 1, end – 1);}
• Рекурсивно обръщане на масив– Ако краищата на масива са се
разминали, се връщаме обратно
– Разменяме крайните елементи на масива
– Обръщаме подмасива от елемент 1 до елемент n-2
Copyright © 2013 DAVID Holding Company
Примери за рекурсия – дв. търсене в сорт. масив
• Двоично търсене в сортиран масив– Ако краищата на масива са се
разминали, се връщаме неуспешно– Взимаме средния елемент и
стойността му наричаме „ключ“– Ако ключът:
• е по-малък от елемента, който търсим,търсим в дясно стоящия подмасив
• е по-голям от елемента, който търсим,търсим в ляво стоящия подмасив
• съвпада с елемента, който търсим,връщаме индекса му
Copyright © 2013 DAVID Holding Company
Примери за рекурсия – търсене на път в лабиринт
• Търсене на път в лабиринт– Рекурсивното търсене на път в лабиринт се основава на
принципа „проба-грешка“– На всяка стъпка:
1. Проверяваме дали сме стигнали. Ако да, се връщаме успешно.2. Маркираме настоящата позиция като настъпана3. Стъпваме във всяка една посока, докато не се
изчерпат и/или не сме намерили пътя
Copyright © 2013 DAVID Holding Company
Задачи за упражнение
• Създайте програма със следните функции:– CreateMatrix, която създава масив от елементи от
тип ConsoleColor попълнен произволно с бяло(White) и черно (Black).
– ShowMatrix, която визуализира създадения масив поподходящ начин
– FillMatrix, която започва да запълва бял регион с червено използвайки рекурсивния flood fill алгоритъм, започвайки от произволна бяла позиция.
– Нека във всяко рекурсивно извикване на FillMatrix се извиква и ShowMatrix, така че визуализираният масив да се опреснява.
Copyright © 2013 DAVID Holding Company
Задачи за упражнение
• Направете рекурсивна функция, която да намира и изпечатва наименованията на всички файлове и техните големини намиращи се в папка с подадено име и на подадена дълбочина в нейните подпапки.– За да вземете списък с наименованията на папките в дадена
папка, използвайтеstring[] folderNames = Directory.GetDirectories(path);
– За да вземете списък с наименованията на файловете в дадена папка, използвайте:string[] fileNames = Directory.GetFiles(path);
– За да вземете големината на файл, използвайте код като следния:long length = new FileInfo(fileName).Length;
Copyright © 2013 DAVID Holding Company
Въпроси?
Copyright © 2013 DAVID Holding Company
Благодаря!
• Валери Дачев– [email protected]– @vdachev– https://facebook.com/vdachev
• ДАВИД академия– [email protected]– http://acad.david.bg/– @david_academy– https://facebook.com/DavidAcademy