- •Розділ 2. Задача та її аналіз
- •2.1. Етапи розв’язання задач:
- •2.1.1. Постановка задачі
- •2.1.2. Побудова моделі
- •2.1.3. Розробка алгоритму
- •2.1.4. Перевірка правильності алгоритму
- •2.1.5. Аналіз алгоритму і його складності
- •2.1.6. Реалізація алгоритму
- •2.1.7. Перевірка програми
- •2.1.8. Документація
- •2.2. Типи задач
- •2.3. Принцип декомпозиції
- •2.4. Логічна схема розрахунку
- •2.5. Методи розробки алгоритмів
- •2.5.1. Методи приватних цілей, підйому і відпрацювання назад
- •2.5.2. Евристики
- •2.5.3. Рекурсія
- •Заключні зауваження
2.5.2. Евристики
Евристичний алгоритм, чи евристика, визначається як алгоритм із наступними властивостями:
Він звичайно знаходить гарні, хоча не обов'язково оптимальні рішення.
Його можна швидше і простіше реалізувати, чим будь-який відомий точний алгоритм (тобто той, який гарантує оптимальне рішення).
Часто дуже гарні алгоритми повинні розглядатися як евристичні. Наприклад, припустимо, що ми побудували швидкий алгоритм, що, здається, працює на всіх тестових задачах, але ми не можемо довести, що алгоритм правильний. Поки не знайдено такий доказ, алгоритм варто розглядати як евристику.
Програмування з відходом назад.
Метод розробки алгоритму, відомий як програмування з відходом назад, можна описати як організований вичерпний пошук, що часто дозволяє уникнути дослідження всіх можливостей. Цей метод особливо зручний для розв’язку задач, що вимагають перевірки потенційно великого, але кінцевого числа розв’язків.
Програмування з відходом часто називають «методом на крайній випадок», тому що можна привести аргументи, що показують, що воно усього лише на один рівень ефективніший «лобових» методів повного перебору. Потрібно, звичайно, шукати більш ефективні методи, але таке побажання легше висловити, чим здійснити. Програмування з відходом — важливий інструмент, яким повинний уміти користуватися кожний програміст.
Метод гілок і границь
Метод, відомий як метод гілок і границь, схожий на методи з відходами назад тим, що він досліджує деревоподібну модель простору рішень і застосовується для широкого кола дискретних комбінаторних задач. Алгоритми з відходами націлені на те, щоб знайти одну чи всі конфігурації, що моделюються N-векторами, що задовольняють визначені властивості. Алгоритми віток і границь орієнтовані в більшому ступені на оптимізацію. У розв'язуваній задачі визначена числова функція вартості для кожної з вершин, що з'являються в дереві пошуку. Мета — знайти конфігурацію, на якій функція вартості досягає максимального чи мінімального значення.
Алгоритм гілок і границь добре працює в задачі. Слід зазначити, що алгоритми віток і границь, як правило, бувають досить складним. За допомогою алгоритмів віток і границь вдалося вирішити велике коло різноманітних серйозних практичних задач, однак ці алгоритми рідко виявляються простими.
2.5.3. Рекурсія
У математиці і програмуванні рекурсія — це метод визначення чи вираження функції, процедури, чи мовної конструкції рішення задачі за допомогою тієї ж функції, процедури і т.д. Кілька прикладів додадуть цій ідеї більш конкретний характер.
Факторіали і числа Фібоначчі
Факторіальна функція визначається рекурсивно в такий спосіб:
O!=l, N!=N (N—l)!, якщо N>0,
чи у виді фрагмента програми:
FAC(0)=1, (2.1)
FAC(N)=N*FAC(N—l), якщо N>0 (2.2)
Областю визначення функції FAC є безліч ненегативних цілих чисел.
Рівняння (2.2) — це приклад рекурентного співвідношення. Рекурентні співвідношення виражають значення функції за допомогою інших значень, обчислених для менших аргументів. Рівняння (2.1) - нерекурсивно визначене значення функції. Для кожної рекурсивної функції потрібно хоча б одне таке початкове значення, у противному випадку її не можна обчислити в явному виді.
Аналогічно числа Фібоначчі визначаються наступною нескінченною послідовністю цілих чисел: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, .... Перевірка показує, що N-й елемент цієї послідовності дорівнює сумі двох безпосередньо попередніх елементів. Таким чином, якщо FIB(N) може бути визначене з рекурентного співвідношення:
FIB(N)=FIB(N-1)+FIB(N-2).
Тому що FIB(N) визначено через два різних значення для менших аргументів, необхідні два початкових значення. Ними служать
FIB(1)=1,
FIB(2)=1.
Функція Акермана
Дві рекурсивні функції, розглянуті дотепер, були досить простими. Тому, щоб не склалося неправильне враження про те, наскільки складні рекурсивні функції, представимо наступну, досить просту на перший погляд, двічі рекурсивну функцію, відому як функція Акермана. Функція двічі рекурсивна, якщо сама функція й один з її аргументів визначені через самих себе.
А(М,
N}=
Розбивки цілого
Розбивка позитивного цілого числа М — це представлення М в виді суми цілих чисел. Класична рахункова задача — визначення кількості Р(М} розбивок числа М. Для М=6 розбивками є
6
5+1
4+2, 4+1+1
3+3, 3+2+1, 3+1+1+1
2+2+2, 2+2+1+1, 2+1+1+1+1
1+1+1+1+1+1.
Цікавий спосіб вираження функції Р(М) через іншу функцію Q(M, N), що визначається як число розбивок цілого М із що складаються, не перевищуючими N. Нескладні рекурсивні міркування приводять до наступного:
Q(M, 1)=1. Це значить, що існує тільки одна розбивка цілого числа М, у якому найбільший доданок є одиниця, а саме M=1+1+. . .+1.
Q(1, N)=1. Очевидно, існує тільки одна розбивка цілого числа 1 незалежно від величини N найбільшого доданка.
Q(M, N)=Q(M, M), якщо M<N. Зрозуміло, що ніяка розбивка М не може містити доданку N, більшого M.
Q(M, M}=1+Q(M, М—1). Існує тільки одна розбивка М з доданком, рівним М. Всі інші розбивки М мають найбільший доданок NM—1.
Q(M, N)=Q(M, N—l}+Q(M—N, N). Це означає, що будь-яка розбивка М з найбільшим доданком, меншим чи рівним N, чи не містить N як доданок — у такому випадку дана розбивка також входить у число Q(M, N—1), або містить N — при цьому інші доданки утворять розбивка числа М-N.
Рівняння 1÷5 рекурсивно визначають функцію Q(M, N). Вони визначають Р(М), тому що P(M)=Q{M, M).
Пошук у глибину й у ширину
У багатьох мережних теоретичних задачах потрібно знайти довільне дерево в мережі G (наприклад, для визначення, чи зв'язна G) чи перевірити кожне ребро G хоча б по разу. Був запропонований простий рекурсивний алгоритм для цих двох задач. Алгоритм називається пошуком у глибину. Його основна ідея може бути застосована в багатьох ситуаціях, коли потрібно знайти якийсь оптимум у результаті пошуку за великою структурою типу дерева.
Рекурсія в порівнянні з ітерацією
Повний розгляд переваг і недоліків рекурсивних і ітераційних алгоритмів лежить за межами цього посібника. Так само обстоїть справа і з розглядом зв'язків між рекурсією й ітерацією, що представляють глибокий теоретичний інтерес. Дехто стверджував, що будь-який рекурсивний алгоритм варто перетворювати в еквівалентний ітеративний алгоритм. Інші затверджують, що це не завжди має сенс робити.
Рекурсія - це природний метод представлення алгоритмів розв’язку безлічі математичних і обчислювальних задач і, що це метод, про який не слід забувати. Як тільки алгоритм рекурсивно сформульований, виникає кілька варіантів реалізації. Перший і найбільш очевидний – реалізувати його мовою програмування, що допускає рекурсію. Другий варіант – реалізувати його нерекурсивною мовою, моделюючи стек, як ми робили в алгоритмі DFS. Третя можливість - звернутися по допомогу до літератури по різним «автоматичним» процедурам перетворення рекурсивного алгоритму в ітеративний. Нарешті, можна переглянути задачу для того, щоб з'ясувати, чи дійсно рекурсія необхідна, тобто можна побудувати новий алгоритм.
Щоб розробити алгоритм для моделювання конкретної системи "одна черга/один обслуговуючий пристрій", потрібно з'ясувати ряд питань:
Які розподіли ймовірностей проміжків часу між прибуттями клієнтів і тривалості обслуговування?
Яким датчиком випадкових чисел і як користуватися для одержання випадкових проміжків часу?
Які правила в черзі (властивості черги)? Наприклад, чи підуть першими клієнти, що мають найвищий пріоритет? Чи будуть нетерплячі клієнти залишати чергу через те, що вона занадто велика чи занадто повільно рухається? Чи обмежене число клієнтів, що одночасно можуть знаходитися в черзі?
Скільки часу варто продовжувати процес імітації? Чи має годинник відраховувати одиниці часу, чи вони повинні переходити до моменту наступної визначеної події?
Як будуть «складені розклади» для подій, щодо яких установлено (чи випадково не випадково), що повинно відбутися у визначений час? Іншими словами, як ми переконаємося, що ці події і всі інші, що є їхнім наслідком, дійсно відбуваються і відбуваються вчасно? Що робити у випадку «збігу», тобто коли дві чи більш події случаються одночасно?
Які дані потрібно зібрати під час процесу імітації? Типові дані, в одержанні яких могла б виникнути зацікавленість, що випливають:
Число прибуття в процесі імітації.
Середня довжина черги.
Середній час чекання в черзі.
Максимальна довжина черги.
Ефективність використання обслуговуючого пристрою, тобто відсоток часу, протягом якого обслуговуючий пристрій був зайнятий.
Середній, максимальний і мінімальний час обслуговування.
Функція розподілу довжини черги, тобто процентні частки часу, протягом якого довжина черги була t. Число клієнтів, яким не довелося чекати.
