Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab_4-Recursija (2013).doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
250.88 Кб
Скачать

41

Лабораторна робота № 4

"Побудова та аналіз складності рекурсивних алгоритмів"

1. МЕТА РОБОТИ

Вивчити основні методи організації рекурсивних алгоритмів та дослідження їх ефективності

2. ТЕОРЕТИЧНІ ВІДОМОСТІ

2.1. Рекурсія

Термін рекурсія дуже популярний у математиці і програмуванні. Цей термін походить від латинського слова recursio — повернення. У широкому сенсі, рекурсія - це звертання до самого себе. Популярність рекурсії в програмуванні у значній мірі пояснюється тим, що вона робить стиль програмування більш декларативним. Це означає, що при складанні алгоритму розв’язання задачі програміст іноді може схитрувати — звести розв’язання цієї задачі до розв’язання тієї ж самої задачі, але при більш простих початкових умовах. Тим самим, він як би «перекладає на плечі» комп'ютера складання дрібних деталей алгоритму. Якщо ж складання всіх навіть самих дрібних деталей алгоритму програміст залишає за собою, його стиль програмування називають не декларативним, а процедурним.

Як приклад розглянемо задачу знаходження максимального серед N чисел. Можна запропонувати наступний спосіб її розв'язання:

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

На перший погляд у цьому рішенні є якась недомовленість — для знаходження максимального серед N чисел пропонується знайти максимальне серед N-1 числа. Але, як це не парадоксально, представлений рекурсивний алгоритм розв’язання цієх задачі є цілком коректним. А програма, у якій цей алгоритм реалізований, видає правильний результат.

2.2. Рекурсивні функції

Рекурсивна функція — це така функція, серед операторів якої є оператор виклику самої цієї функції.

Якби при роботі рекурсивної функції рекурсивні виклики виконувалися без всяких умов, її робота ніколи б не закінчилася. Тому серед операторів рекурсивної функції обов'язково повинна бути умова завершення рекурсії.

Послідовність рекурсивних викликів називається рекурсивним спуском. Номер рекурсивного виклику в цій послідовності називається глибиною рекурсії.

Серед операторів рекурсивної функції можуть бути такі, котрі виконують деяку дію і які розташовані до оператора рекурсивного виклику. Вони виконуються в тому порядку, у якому відбуваються рекурсивні виклики. Говорять, що така дія виконується на рекурсивному спуску.

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

Приклади застосування рекурсивних функцій:

1) Обчислення факторіала натурального числа з виконанням дій на рекурсивному спуску:

#include <iostream.h>

long Factorial(int ch,int ind, long D){

long F;

D=D*ind;

if((ch==0)||(ind==ch))

F=D;

else F=Factorial(ch,ind+1,D);

return F;

}

int main(void){

int s,t,y;

t=1;

y=1;

s=7; //будь-яке число, можно зробити ввід з клавіатури;

long H=Factorial(s,t,y);

cout<<H<<endl;

return 0;

}

Функція Factorial є функцією трьох аргументів. Крім натурального числа, факторіал якого вона повертає, у неї є ще два аргументи: індекс і добуток. Їх початкові значення дорівнюють одиниці.

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

2) Обчислення факторіала натурального числа з виконанням дій на рекурсивному підйомі:

#include <iostream.h>

long Factorial(int ch){

long F;

long D;

if((ch==0)||(ch==1))

{

ch=1; D=1;

}

else D=Factorial(ch-1);

return F=D*ch;

}

int main(void){

int s;

s=7; //будь-яке число, можно зробити ввід з клавіатури;

long H=Factorial(s);

cout<<H<<endl;

return 0;

}

2.3. Аналіз складності рекурсивних алгоритмів

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

Якщо в алгоритмі відбувається розділення завдання на b підзадач, яке приводить до необхідності рішення a підзадач розмірністю n/b, то функцію трудомісткості можна представити у вигляді

(2.1)

де d(n) — трудомісткість алгоритму ділення завдання на підзадачі,

U(n) - трудомісткість алгоритму об'єднання отриманих рішень.

Розглянемо, наприклад, відомий алгоритм сортування злиттям, належний Дж. Фон Нейману. На кожному рекурсивному виклику переданий масив ділиться навпіл, що дає оцінку для d(n)= , далі рекурсивно викликається сортування отриманих масивів половинної довжини (до тих пір, поки довжина масиву не стане рівною одиниці), і повернені відсортовані масиви об'єднуються з трудомісткістю . Тоді очікувана трудомісткість сортування складе:

Тим самим виникає питання про отримання оцінки складності функції трудомісткості, заданої у вигляді (2.1), для довільних цілих значень a i b . Відповідь на це питання можна отримати на основі теореми про рекурентні співвідношення, авторами якої є Дж. Бентлі, Д. Хакен і Дж. Сакс. Приведемо її формулювання.

Теорема Д.2.1. Нехай - константи, g(n) – функція, нехай далі , де , або , тоді

(1) Якщо , то .

Приклад: , тоді

(2) Якщо , то .

Приклад: , тоді

(3) Якщо , то .

Приклад: , тут підходить випадок (3), оскільки

, відповідно

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

На підставі цієї теореми отримаємо оцінку складності функції трудомісткості для алгоритму сортування злиттям. Оскільки в цьому випадку , то .

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]