Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab_2-Analiz.doc
Скачиваний:
19
Добавлен:
28.08.2019
Размер:
389.63 Кб
Скачать

2.7. Методика аналізу основних алгоритмічних конструкцій

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

1) Лінійна конструкція. Трудомісткість конструкції є сума трудомісткостей блоків, що ідуть один за одним

f лінійної конструкції = f1 + ... + fk ,

де k - кількість блоків в конструкції.

2) Конструкція розгалуження.

if ( L ) {

блок із трудомісткістю f then , що виконується з імовірністю р }

else {

блок із трудомісткістю f else , що виконується з імовірністю (1-р)}

В цій конструкції ( L ) означає логічний вираз, який складається з логічних змінних і/або арифметичних, символьних та інших по типу порівнянь з обмеженням Ө(1) на трудомісткість їх обчислення. Імовірності переходів на відповідні блоки можуть мінятися, як залежно від даних, так і залежно від параметрів зовнішніх циклів і/або інших умов. Досить важко дати які-небудь загальні рекомендації для одержання р без прив'язки до конкретного алгоритму або особливостей входу.

Загальна трудомісткість конструкції розгалуження для побудови функції трудомісткості в середньому випадку вимагає знаходження ймовірності р виконання переходів на блоки «then» і «else». При відомому значенні ймовірності р трудомісткість конструкції визначається як

f конструкції розгалуження = f1 + p · f then + (1-р) · f else,

де f1 - трудомісткість обчислення умови ( L ).

У загальному випадку ймовірність р є функція вхідних даних і пов'язаних з ними проміжних результатів р =p(D).

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

3) Конструкція циклу. Розглянемо спочатку реалізацію циклу за лічильником:

for (i=0; i<n; ++i) { // i=0 (1 операція )

тіло циклу // тіло циклу ( K т.ц. операцій )

} // i=i+1; перевірка умови i<n (3 операції на кожний один прохід циклу)

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

f конструкції циклу = 1 + n · ( K т.ц. +3)

Реалізація циклу за умовою не міняє методику оцінки його складності.

Аналіз вкладених циклів по лічильнику з незалежними індексами зводиться до занурення трудомісткості внутрішнього циклу в трудомісткість тіла зовнішнього циклу.

Для вкладених залежних циклів трудомісткість визначається у вигляді вкладених сум із залежними індексами.

2.8. Приклади аналізу трудомісткості алгоритмів

Як приклади застосування методики для аналізу алгоритмів по функції трудомісткості розглянемо декілька алгоритмів, що відносяться до класу N і різним підкласам класу NPR .

Приклад 1. Алгоритм знаходження суми елементів квадратної матриці.

int suma (int a[][],const int n){

int i, j, s = 0;

1 операція

for (i=0; i<n; i++) {

1 операція. Всього n проходів циклу

for (j=0; j<n; j++) {

1 операція. Всього n проходів циклу

s = s + a[i][j];

4 операції на кожний прохід циклу

}

3 операції на кожний прохід циклу

}

3 операції на кожний прохід циклу

return s;

}

Цей алгоритм виконує однакову кількість операцій при фіксованому значенні n. Таким чином, він є кількісно-залежним і належить до класу N. Функція трудомісткості обчислюється для цього алгоритму за формулою:

Приклад 2. Алгоритм пошуку максимального елемента масиву.

int maximum(int a[],const int n){

int max = a[0];

2 операції

for(int i=1; i<n; i++) {

1 операція. Всього (n-1) проходів циклу

if (a[i] > max)

2 операції на кожний прохід циклу

max = a[i];

2 операції на кожний прохід циклу

}

3 операції на кожний прохід циклу

return max;

}

Стосовно операцій порівняння цей алгоритм є кількісно-залежним, оскільки завжди треба перевиряти всі n елементів. Але кількість операцій присвоювання залежить від місця, де саме розташований найбільший елемент в послідовності, тобто стосовно операцій перестановок цей алгоритм є кількісно-параметрично залежним. Отже, в загальному даний алгоритм є кількісно-параметрично залежним і належить до класу NPRS. Тому при фіксованому розмірі вхідних даних необхідно проводити окремий аналіз для найгіршого, найкращого та середнього випадків.

1) Найгірший випадок – найбільша кількість операцій присвоювання (max=a[i]) буде тоді, коли елементи масиву посортовані по зростанню. Трудомісткість алгоритму в такому випадку рівна:

2) Найкращий випадок – мінімальна кількість переприсвоювань максимального числа буде в тому випадку, коли максимальний елемент розташований на першому місці в масиві. Трудомісткість алгоритму в такому випадку рівна:

3) Середній випадок. Алгоритм пошуку максимуму послідовно перебирає елементи масиву, порівнюючи поточний елемент масиву з поточним значенням максимуму. На черговому кроці, коли проглядається і-ий елемент масиву, переприсвоювання максимуму відбудеться тоді, коли в підмасиві з перших і елементів максимальним елементом буде останній.

Очевидно, що у випадку рівномірного розподілу a[i], ймовірність того, що максимальний з і елементів розташований в певній (останній) позиції дорівнює 1/і . Тоді в масиві з n елементів загальна кількість операцій присвоювання визначається як:

де γ0.577216 . . . (ця сума називається n-им гармонійним числом и позначається Hn; константа γ називається константою Ейлера).

Таким чином, значення середньої кількості операцій присвоювання в алгоритмі пошуку максимума в масиві з n елементів визначається як:

Приклад 3. Алгоритм сортування по зростанню масиву методом простої вставки з "просіюванням".

void sort (int a[],const int n){

int i, j, x ;

for (i=1; i<n; i++) {

1 операція. Всього (n-1) проходів циклу for

x = a[i];

2 операції на кожний прохід циклу for

j = i-1;

2 операції на кожний прохід циклу for

while ((j>=0) && (a[j]>x)) {

4 операції на кожний прохід циклу for

a[j+1] = a[j];

4 операції на кожний прохід циклу while

j-=1;

2 операції на кожний прохід циклу while

a[j+1] = x;

3 операції на кожний прохід циклу while

}

}

3 операції на кожний прохід циклу for

}

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

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

Найгірший випадок – найбільша кількість операцій переміщень буде тоді, коли елементи масиву посортовані по спаданню. Спочатку порахуємо максимальну кількість переміщень для всіх елементів. Перший елемент масиву a[0] здійснить 0 переміщень, наступний елемент a[1] здійснить 1 переміщення і т.д. , останній елемент масиву a[n-1] здійснить (n-1) переміщення. Отже:

Трудомісткість алгоритму в такому випадку рівна:

2) Найкращий випадок – мінімальна кількість переміщень буде тоді, коли елементи масиву посортовані по зростанню і непотрібно виконувати жодного переміщення. Трудомісткість алгоритму в такому випадку рівна:

3) Середній випадок. Для аналізу трудомісткості в середньому спочатку визначимо середню кількість порівнянь для і-го елемента:

Підсумуємо для всіх (n-1) елементів, які треба вставити:

Використовуючи методику аналізу алгоритмічних конструкцій, остаточно маємо:

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