НОУ ІНТУЇТ | лекція | Рекурсія, ітерація і оцінки складності алгоритмів

Основи оцінок складності алгоритмів

Нам вже відомо, що правильність - далеко не єдине якість, яким повинна володіти хороша програма. Одним з найважливіших є ефективність, що характеризує насамперед час виконання програми Нам вже відомо, що правильність - далеко не єдине якість, яким повинна володіти хороша програма для різних вхідних даних (параметра ).

Знаходження точної залежності Знаходження точної залежності   для конкретної програми - завдання досить складна для конкретної програми - завдання досить складна. З цієї причини зазвичай обмежуються асимптотическими оцінками цієї функції, тобто описом її зразкової поведінки при великих значеннях параметра . Іноді для асимптотичних оцінок використовують традиційне ставлення (Читається "Про велике") між двома функціями , Визначення якого можна знайти в будь-якому підручнику з математичного аналізу, хоча частіше застосовують відношення еквівалентності (Читається "тета велике"). Його формальне визначення є, наприклад, в книзі [8] , Хоча нам поки досить буде розуміння даного питання в загальних рисах.

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

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

Не менш цікавий і приклад обчислення Не менш цікавий і приклад обчислення   -го числа Фібоначчі -го числа Фібоначчі. У процесі її дослідження фактично вже було з'ясовано, що її складність є експоненційної і дорівнює . Подібні програми практично не застосовні на практиці. У цьому дуже легко переконатися, спробувавши обчислити з її допомогою 40-е число Фібоначчі. З цієї причини цілком актуальна наступне завдання.

Завдання 5.4. Напишіть програму, що друкує Завдання 5 -е число Фібоначчі, яка мала б лінійну складність.

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

текст програми

public class FibIv1 {public static void main (String [] args) throws Exception {int n = Xterm.inputInt ( "Введіть n ->"); Xterm.print ( "f (" + n + ")"); if (n <0) {Xterm.print ( "не визначене \ n"); } Else if (n <2) {Xterm.println ( "=" + n); } Else {long i = 0; long j = 1; long k; int m = n; while (--m> 0) {k = j; j + = i; i = k; } Xterm.println ( "=" + j); }}}

Наступне питання цілком природний - а чи можна знаходити числа Фібоначчі ще швидше?

Після вивчення певних розділів математики зовсім просто вивести таку формулу для Після вивчення певних розділів математики зовсім просто вивести таку формулу для   -ого числа Фібоначчі   , Яку легко перевірити для невеликих значень   : -ого числа Фібоначчі , Яку легко перевірити для невеликих значень :

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

текст програми

public class FibIv2 {public static void main (String [] args) throws Exception {int n = Xterm.inputInt ( "Введіть n ->"); double f = (1.0 + Math.sqrt (5.)) / 2.0; int j = (int) (Math.pow (f, n) / Math.sqrt (5.) + 0.5); Xterm.println ( "f (" + n + ") =" + j); }}

Насправді ця програма використовує виклик функції зведення в ступінь {Math.pow (f, n)}, яка не може бути реалізована швидше, ніж за логарифмічна час ( Насправді ця програма використовує виклик функції зведення в ступінь {Math ). Про алгоритми, в яких кількість операцій приблизно пропорційно (В інформатиці прийнято не вказувати підставу довічного логарифма) говорять, що вони має логарифмічну складність ( ).

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

Завдання 5.5. Напишіть програму, що друкує Завдання 5 -е число Фібоначчі, яка мала б логарифмічну складність.

текст програми

public class FibIv3 {public static void main (String [] args) throws Exception {int n = Xterm.inputInt ( "Введіть n ->"); Xterm.print ( "f (" + n + ")"); if (n <0) {Xterm.println ( "не визначено"); } Else if (n <2) {Xterm.println ( "=" + n); } Else {Matrix b = new Matrix (1, 0, 0, 1); Matrix c = new Matrix (1, 1, 1, 0); while (n> 0) {if ((n & 1) == 0) {n >>> = 1; c.square (); } Else {n - = 1; b.mul (c); }} Xterm.println ( "=" + b.fib ()); }}} Class Matrix {private long a, b, c, d; public Matrix (long a, long b, long c, long d) {this.a = a; this.b = b; this.c = c; this.d = d; } Public void mul (Matrix m) {long a1 = a * m.a + b * mc; long b1 = a * m.b + b * md; long c1 = c * m.a + d * mc; long d1 = c * m.b + d * md; a = a1; b = b1; c = c1; d = d1; } Public void square () {mul (this); } Public long fib () {return b; }}

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

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

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

Таблиця 5.1. Порівняльна таблиця часів виконання алгоритмів Складність алгоритму n = 10 n = 103 n = 106 log n 0.2 сек. 0.6 сек. 1.2 сек. n 0.6 сек. 1 хв. 16.6 годину. n2 6 сек. 16.6 годину. 1902 року 2n 1 хв. 10295 років 10300000 років

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

Зі збільшенням швидкодії комп'ютерів зростають і значення параметрів, для яких робота того чи іншого алгоритму завершується за прийнятний час. Таким чином, збільшується середнє значення величини Зі збільшенням швидкодії комп'ютерів зростають і значення параметрів, для яких робота того чи іншого алгоритму завершується за прийнятний час , І, отже, зростає величина відносини часів виконання швидкого і повільного алгоритмів. Чим швидше комп'ютер, тим більше відносний програш при використанні поганого алгоритму!

Чи можна обчислити факторіал швидше?

Уважаемые партнеры, если Вас заинтересовала наша продукция, мы готовы с Вами сотрудничать. Вам необходимо заполнить эту форму и отправить нам. Наши менеджеры в оперативном режиме обработают Вашу заявку, свяжутся с Вами и ответят на все интересующее Вас вопросы.

Или позвоните нам по телефонам: (048) 823-25-64

Организация (обязательно) *

Адрес доставки

Объем

Как с вами связаться:

Имя

Телефон (обязательно) *

Мобильный телефон

Ваш E-Mail

Дополнительная информация: