Жадібний алгоритм із прикладом: що таке, метод і підхід
Що таке жадібний алгоритм?
In Жадібний алгоритм набір ресурсів рекурсивно поділяється на основі максимальної, негайної доступності цього ресурсу на будь-якій даній стадії виконання.
Для вирішення проблеми, заснованої на жадібному підході, є два етапи
- Сканування списку предметів
- Оптимізація
Ці етапи розглядаються паралельно в цьому підручнику з алгоритму Greedy під час поділу масиву.
Щоб зрозуміти жадібний підхід, вам потрібно мати практичні знання про рекурсію та перемикання контексту. Це допоможе вам зрозуміти, як відстежити код. Ви можете визначити жадібну парадигму в термінах ваших власних необхідних і достатніх тверджень.
Парадигму жадібності визначають дві умови.
- Кожне поетапне рішення має структурувати проблему в напрямку її найкраще прийнятного рішення.
- Досить, якщо структурування проблеми може зупинитися на кінцевій кількості жадібних кроків.
Продовжуючи теоретизування, давайте опишемо історію, пов’язану з підходом Greedy Search.
Історія Greedy Algorithms
Ось важливий орієнтир жадібних алгоритмів:
- Жадібні алгоритми були розроблені для багатьох алгоритмів блукання по графу в 1950-х роках.
- Есджер Джікстра концептуалізував алгоритм для створення мінімальних остовних дерев. Він мав на меті скоротити проміжок маршрутів у столиці Нідерландів Амстердамі.
- У тому ж десятилітті Прім і Крускал розробили стратегії оптимізації, які ґрунтувалися на мінімізації вартості шляху уздовж зважених маршрутів.
- У 70-х роках американські дослідники Кормен, Рівест і Стайн запропонували рекурсивну підструктуру жадібних рішень у своїй класичній книзі «Вступ до алгоритмів».
- Парадигма жадібного пошуку була зареєстрована як інший тип стратегії оптимізації в записах NIST у 2005 році.
- До теперішнього часу протоколи, які запускають Інтернет, такі як відкритий найкоротший шлях спочатку (OSPF) і багато інших мережевих протоколів комутації пакетів, використовують жадібну стратегію для мінімізації часу, проведеного в мережі.
Жадібні стратегії та рішення
Логіка в її найлегшій формі зводилася до «жадібних» або «не жадібних». Ці твердження були визначені підходом, використаним для просування на кожному етапі алгоритму.
Наприклад, алгоритм Djikstra використовував покрокову жадібну стратегію ідентифікації хостів в Інтернеті шляхом обчислення функції вартості. Значення, яке повертає функція вартості, визначає, чи є наступний шлях «жадібним» чи «нежадібним».
Коротше кажучи, алгоритм перестає бути жадібним, якщо на будь-якому етапі він робить крок, який не є локально жадібним. Жадібні проблеми припиняються без подальшого прояву жадібності.
Характеристики жадібного алгоритму
Важливими характеристиками жадібного алгоритму є:
- Існує впорядкований список ресурсів із розподілом витрат або цінностей. Вони кількісно визначають обмеження системи.
- Ви отримаєте максимальну кількість ресурсів протягом часу, на який діє обмеження.
- Наприклад, у задачі планування діяльності витрати на ресурси вказані в годинах, і дії потрібно виконувати в послідовному порядку.
Навіщо використовувати жадібний підхід?
Ось причини використання жадібного підходу:
- Жадібний підхід має кілька компромісів, які можуть зробити його придатним для оптимізації.
- Однією з важливих причин є негайне досягнення найбільш прийнятного рішення. У задачі вибору діяльності (пояснення нижче), якщо до завершення поточної дії можна виконати більше дій, ці дії можна виконати протягом того самого часу.
- Інша причина полягає в тому, щоб рекурсивно розділити задачу на основі умови, без необхідності комбінувати всі рішення.
- У задачі вибору діяльності крок «рекурсивного поділу» досягається шляхом сканування списку елементів лише один раз і врахування певних дій.
Як вирішити проблему вибору діяльності
У прикладі планування діяльності для кожної дії є час «початку» та «закінчення». Кожна діяльність індексується номером для довідки. Є дві категорії діяльності.
- розглянута діяльність: це Діяльність, яка є посиланням, за яким аналізується здатність виконувати більше ніж одну дію, що залишилася.
- решта діяльності: діяльності на один або більше індексів, випереджаючи розглянуту діяльність.
Загальна тривалість дає вартість виконання діяльності. Тобто (фініш – початок) дає нам тривалість як вартість діяльності.
Ви дізнаєтесь, що жадібний екстент — це кількість дій, які ви можете виконати за час розглядуваної діяльності.
Archiструктура підходу Greedy
КРОК 1) Проскануйте список витрат на діяльність, починаючи з індексу 0 як розглянутого Індексу.
КРОК 2) Коли більше дій можна буде завершити до того часу, розглянута діяльність завершиться, почніть пошук однієї або кількох дій, що залишилися.
КРОК 3) Якщо дій, що залишилися, більше немає, поточна діяльність, що залишилася, стає наступною. Повторіть крок 1 і крок 2 з новою розглянутою діяльністю. Якщо не залишилося жодних дій, перейдіть до кроку 4.
КРОК 4) Повернути об'єднання розглянутих індексів. Це індекси активності, які будуть використовуватися для максимізації пропускної здатності.
Пояснення коду
#include<iostream> #include<stdio.h> #include<stdlib.h> #define MAX_ACTIVITIES 12
Пояснення коду:
- Включені файли заголовків/класи
- Максимальна кількість дій, які надає користувач.
using namespace std; class TIME { public: int hours; public: TIME() { hours = 0; } };
Пояснення коду:
- Простір імен для потокових операцій.
- Визначення класу для TIME
- Часова позначка години.
- Конструктор TIME за замовчуванням
- Години змінні.
class Activity { public: int index; TIME start; TIME finish; public: Activity() { start = finish = TIME(); } };
Пояснення коду:
- Визначення класу з діяльності
- Мітки часу, що визначають тривалість
- Усі мітки часу ініціалізуються нулем у конструкторі за замовчуванням
class Scheduler { public: int considered_index,init_index; Activity *current_activities = new Activity[MAX_ACTIVITIES]; Activity *scheduled;
Пояснення коду:
- Частина 1 визначення класу планувальника.
- Розглянутий індекс є відправною точкою для сканування масив.
- Індекс ініціалізації використовується для призначення випадкових позначок часу.
- Масив об’єктів діяльності динамічно розподіляється за допомогою оператора new.
- Запланований покажчик визначає поточне базове розташування для greed.
Scheduler() { considered_index = 0; scheduled = NULL; ... ...
Пояснення коду:
- Конструктор планувальника – частина 2 визначення класу планувальника.
- Розглянутий індекс визначає поточний початок поточного сканування.
- Поточний greedy екстент не визначений на початку.
for(init_index = 0; init_index < MAX_ACTIVITIES; init_index++) { current_activities[init_index].start.hours = rand() % 12; current_activities[init_index].finish.hours = current_activities[init_index].start.hours + (rand() % 2); printf("\nSTART:%d END %d\n", current_activities[init_index].start.hours ,current_activities[init_index].finish.hours); } … …
Пояснення коду:
- Цикл for для ініціалізації годин початку та годин завершення кожної із запланованих на даний момент дій.
- Ініціалізація часу початку.
- Ініціалізація часу завершення завжди після або точно в годину початку.
- Оператор налагодження для друку виділених тривалостей.
public: Activity * activity_select(int); };
Пояснення коду:
- Частина 4 – остання частина визначення класу планувальника.
- Функція вибору активності бере за основу індекс початкової точки та розділяє жадібний квест на жадібні підпроблеми.
Activity * Scheduler :: activity_select(int considered_index) { this->considered_index = considered_index; int greedy_extent = this->considered_index + 1; … …
- За допомогою оператора дозволу області (::) надається визначення функції.
- Розглянутий Індекс є Індексом, який називається за значенням. Greedy_extent — це ініціалізований лише індекс після розглянутого індексу.
Activity * Scheduler :: activity_select(int considered_index) { while( (greedy_extent < MAX_ACTIVITIES ) && ((this->current_activities[greedy_extent]).start.hours < (this->current_activities[considered_index]).finish.hours )) { printf("\nSchedule start:%d \nfinish%d\n activity:%d\n", (this->current_activities[greedy_extent]).start.hours, (this->current_activities[greedy_extent]).finish.hours, greedy_extent + 1); greedy_extent++; } … ...
Пояснення коду:
- Основна логіка – жадібність обмежена кількістю видів діяльності.
- Початкові години поточної дії перевіряються як заплановані перед тим, як розглядувана діяльність (зазначена розглянутим індексом) завершиться.
- Поки це можливо, друкується необов’язковий оператор налагодження.
- Перехід до наступного індексу в масиві активності
... if ( greedy_extent <= MAX_ACTIVITIES ) { return activity_select(greedy_extent); } else { return NULL; } }
Пояснення коду:
- Умовна функція перевіряє, чи виконано всі дії.
- Якщо ні, ви можете перезапустити greedy з розглянутим індексом як поточною точкою. Це рекурсивний крок, який жадібно розділяє формулювання проблеми.
- Якщо так, він повертається до абонента без можливості для розширення жадібності.
int main() { Scheduler *activity_sched = new Scheduler(); activity_sched->scheduled = activity_sched->activity_select( activity_sched->considered_index); return 0; }
Пояснення коду:
- Основна функція, яка використовується для виклику планувальника.
- Створено новий планувальник.
- Функція вибору активності, яка повертає покажчик типу активності, повертається до абонента після завершення жадібного квесту.
вихід:
START:7 END 7 START:9 END 10 START:5 END 6 START:10 END 10 START:9 END 10 Schedule start:5 finish6 activity:3 Schedule start:9 finish10 activity:5
Обмеження жадібної техніки
Він не підходить для жадібних проблем, де потрібне рішення для кожної підпроблеми, наприклад сортування.
У таких практичних задачах алгоритму Greedy метод Greedy може бути неправильним; у гіршому випадку навіть призведе до неоптимального рішення.
Тому недолік жадібних алгоритмів полягає в тому, що вони не знають, що попереду поточного жадібного стану.
Нижче наведено опис недоліків методу Greedy:
У жадібному скануванні, показаному тут у вигляді дерева (вище значення, вище жадібність), стан алгоритму зі значенням: 40, ймовірно, прийме 29 як наступне значення. Крім того, його квест закінчується на 12. Це становить значення 41.
Однак, якщо алгоритм вибрав неоптимальний шлях або прийняв стратегію перемоги. тоді за 25 слідуватиме 40, а загальне покращення витрат становитиме 65, що оцінюється на 24 бали вище як неоптимальне рішення.
Приклади Greedy Algorithms
Більшість мережевих алгоритмів використовують жадібний підхід. Ось список кількох прикладів алгоритму Greedy:
- Алгоритм мінімального остовного дерева Прима
- Проблема продавця подорожі
- Графік – розфарбовування карти
- Алгоритм мінімального остовного дерева Крускала
- Алгоритм мінімального остовного дерева Дейкстри
- Граф – Вершинне покриття
- Проблема з ранцем
- Проблема планування роботи
Підсумки
Підводячи підсумок, у статті було визначено жадібну парадигму, показано, як жадібна оптимізація та рекурсія можуть допомогти вам отримати найкраще рішення до певної точки. Алгоритм Greedy широко використовується для вирішення проблем у багатьох мовах як алгоритм Greedy Python, C, C#, PHP, Javaі т.д. Вибір активності прикладу алгоритму Greedy описано як стратегічна проблема, яка може досягти максимальної пропускної здатності за допомогою жадібного підходу. Зрештою, були пояснені недоліки використання жадібного підходу.