Як довести жадібний алгоритм правильно


29

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

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



Чи можемо ми довести, що жадібний алгоритм правильний, використовуючи матроїд або гредоїд?
zdm

Відповіді:


24

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

Випадкове тестування

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

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

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

Математичні докази правильності

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

Основний принцип - інтуїтивний:

Принцип: Якщо ви ніколи не зробите поганий вибір, ви зробите все гаразд.

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

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

Щоб спростити життя та усунути відволікання, зупинимось на тому випадку, коли немає зв’язків: є єдине, унікальне оптимальне рішення. Вся техніка перенесеться на той випадок, коли може бути кілька однаково хороших оптимів без будь-яких кардинальних змін, але ви повинні бути трохи уважнішими щодо технічних деталей. Почніть з ігнорування цих деталей та зосередження уваги на тому випадку, коли оптимальне рішення унікальне; це допоможе вам зосередитись на тому, що є важливим.

Існує дуже поширена модель підтвердження, яку ми використовуємо. Ми будемо наполегливо працювати, щоб довести наступну властивість алгоритму:

Твердження: Нехай - вихід рішення за алгоритмом, а O - оптимальне рішення. Якщо S відрізняється від O , то ми можемо підправити O , щоб отримати інше рішення O * , який відрізняється від O і строго краще , ніж O .SOSOOOOO

Зверніть увагу, чому це корисно. Якщо твердження вірно, з цього випливає, що алгоритм правильний. Це в основному доказ протиріччя. Або є таким самим, як O, або він різний. Якщо це інше, то ми можемо знайти інше рішення O ∗, що суворо краще, ніж O - але це суперечність, оскільки ми визначили, що O є оптимальним рішенням, і не може бути жодного рішення, яке краще, ніж це. Тому ми змушені зробити висновок, що S не може відрізнятися від O ; S завжди повинен дорівнювати OSOOOOSOSOтобто алчний алгоритм завжди виводить правильне рішення. Якщо ми можемо довести твердження вище, то ми довели, що наш алгоритм правильний.

Чудово. Тож як ми доводимо претензію? Ми розглядаємо рішення як вектор ( S 1 , ... , S n ), який відповідає послідовності n виборів, зроблених алгоритмом, і аналогічно ми вважаємо оптимальним рішення O як вектор ( O 1 , ... , про п ) , відповідний послідовності варіантів , які привели б до O . Якщо S відрізняється від O , повинен існувати деякий індекс i, де S iS(S1,,Sn)nО(О1,,Он)ОSОi ; ми зосередимось на найменших таких я . Тоді ми налаштуємо O ,трохизмінивши O в i- му положенні, щоб відповідати S i , тобто, ми налаштуємо оптимальне рішення O , змінивши i- й вибір на той, який вибрали жадібний алгоритм, а потім ми покажемо, що це призводить до ще кращого рішення. Зокрема, ми визначимо O * бути щощось на зразокSiОiiООiSiОiО

O=(O1,O2,,Oi1,Si,Oi+1,Oi+2,,On),

за винятком того, що часто нам доведеться трохи змінювати частину щоб підтримувати глобальну послідовність. Частина стратегії доказування передбачає певну розумність у належному визначенні O . Тоді м'ясо доведення буде якось використовувати факти про алгоритм і проблему, щоб показати, що O суворо краще, ніж OOi+1,Oi+2,,OnOOO; саме тут вам знадобляться конкретні проблеми. У якийсь момент вам потрібно буде зануритися в деталі вашої конкретної проблеми. Але це дає відчуття структури типового доказу правильності алчного алгоритму.

Простий приклад: Підмножина з максимальною сумою

Це може бути простіше зрозуміти, детально розробивши простий приклад. Розглянемо наступну проблему:

Вхід: набір цілих чисел, ціле число k Вихід: Набір S U розміром k , сума якого максимально більшаUk
SUk

Для цієї проблеми існує природний жадібний алгоритм:

  1. Встановити .S:=
  2. Для : i:=1,2,,k
    • Нехай - найбільше число в U, яке ще не було вибране (тобто i- е найбільше число в U ). Додати е I в S .xiUiUxiS

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

Твердження: Нехай - рішення, що виводиться за цим алгоритмом на вході U , k і O - оптимальне рішення. Якщо S O , то ми можемо побудувати інше рішення O * , сума навіть більше , ніж O .SU,kOSOOO

Доказ. Припустимо , , і нехай я бути індексом першої ітерації , де х явиведення . (Такий індекс i повинен існувати, оскільки ми припустили, що S O і за визначенням алгоритму маємо S = { x 1 , , x k } .) Оскільки (за припущенням) i мінімально, ми повинні мати x 1 , , x i - 1O , і зокрема,SOixiOiSOS={x1,,xk}ix1,,xi1O має вигляд O = { x 1 , x 2 , , x i - 1 , x i , x i + 1 , , x n } , де числа x 1 , , x i - 1 , x i , , x nOO={x1,x2,,xi1,xi,xi+1,,xn}x1,,xi1,xi,,xnперераховані у порядку зменшення. Дивлячись на те, як алгоритм вибирає , ми бачимо, що для всіх j i ми повинні мати x i > x j . Зокрема, x i > x i . Отже, визначимо O = O { x i } { x i } , тобто отримаємо O шляхом видалення i- го числа в Ox1,,xixi>xjjixi>xiO=O{xi}{xi}OiOі додавання . Тепер сума елементів O * є сумою елементів O плюс х I - х ' я , і х I - х ' я > 0 , так що O * «сума s строго більше , ніж O » сума s. Це доводить претензію. xiOOxixixixi>0OO

Інтуїція тут полягає в тому, що якщо жадібний алгоритм коли-небудь робить вибір, несумісний з , то ми можемо довести, що O може бути ще кращим, якби він був модифікований, щоб включати елемент, вибраний жадібним алгоритмом на цьому етапі. Оскільки О є оптимальним, не може бути жодного способу зробити його ще кращим (це було б суперечливістю), тому єдиною залишається можливістю, щоб наше припущення було невірним: іншими словами, жадібний алгоритм ніколи не зробить вибір що не узгоджується з O .OOOO

Цей аргумент часто називають обмінним аргументом або обмінною лемою . Ми знайшли перше місце, де оптимальне рішення відрізняється від жадібного рішення, і ми уявили собі обмін цього елемента на відповідний жадібний вибір (обміняли х ' i на х i ). Деякий аналіз показав, що цей обмін може лише покращити оптимальне рішення - але, за визначенням, оптимальне рішення не можна покращити. Тож єдиний висновок полягає в тому, що не повинно бути місця, де оптимальне рішення відрізняється від жадібного рішення. Якщо у вас є інша проблема, шукайте можливості застосувати цей принцип обміну у вашій конкретній ситуації.Oxixi


Це старе питання, але це перший результат для Google. Рядок then we can tweak O to get another solution O∗ that is different from O and strictly better than Oмене бентежить. Якщо існує кілька оптимальних рішень, можливо, S != Oі те, і інше все ще є оптимальним; ми можемо налаштувати O, щоб бути " strictly better than
схожішим

@citelao, вибачте, що це вас бентежило. На жаль, я не впевнений, як пояснити це більш чітко. Так, може бути кілька оптимальних рішень, всі мають однакове значення. Це правильно. Те, що ви написали, і те, що я написав, є дійсними; немає протиріччя. Різниця полягає в тому, що те, що ви написали, не допомагає довести жадібний алгоритм правильним; те, що я написав, робить. Я можу лише запропонувати переглянути те, що я написав ще раз, і подивитися, чи зможете ви зрозуміти, наскільки корисне те, що я написав. Якщо це не допомагає, можливо, знайдіть інший запис. Я усвідомлюю, що це хитро і заплутано.
DW

1
дякую за швидку відповідь! Я пропустив момент, коли ти зосередився на доведенні алгоритму, якщо він є тільки a single, unique optimal solution. Оскільки це питання стосується доведення будь-якого жадібного алгоритму правильним, я хотів би дати відповідь у випадках, коли існує декілька оптимальних рішень. Минув час, коли я все це вивчив, але хіба це недостатньо, щоб довести, що ви можете обміняти кожен елемент O_i в будь-якому оптимальному рішенні O, що відрізняється від alg. рішення S з S_i і все ще мають рішення O ', який не гірший за O?
citelao

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

Щоб відповісти на ваше останнє запитання, ні, це недостатньо. Це не доводить, що S є оптимальним. (Якщо ви вимагаєте лише, щоб O 'був не гірший за O, бувають випадки, коли S є неоптимальним, але можливо здійснити такий обмін. Отже, доводячи, що можна досягти O', який не гірший, ніж O doesn я нічого не докажу, чи S є оптимальним і чи не підтверджує, що жадібний алгоритм є правильним. Я пропоную трохи більше вивчити метод, описаний у відповіді. Це хитро. Доказ протиріччя часто складно зрозуміти.)
DW

14

Я буду використовувати приклад наступного простого алгоритму сортування:

repeat:
  if there are adjacent items in the wrong order:
     pick one such pair and swap
  else
     break

Для доказу правильності я використовую два кроки.

  • Спочатку я показую, що алгоритм завжди припиняється.
  • Потім я показую, що рішення, де воно припиняється, - це те, що я хочу.

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

Для цього прикладу я вибираю кількість інверсій у вхідному списку. Інверсія у списку - пара записів A [ i ] , A [ j ], така що A [ i ] > A [ j ], але i < j . Кількість інверсій завжди невід'ємна, а відсортований список має 0 інверсій.AA[i]A[j]A[i]>A[j]i<j

A[i]A[i+1]A[i],A[i+1]

Це доводить, що алгоритм з часом припиняється.

Кількість інверсій у відсортованому списку дорівнює 0. Якщо все піде добре, алгоритм зменшить кількість інверсій до 0. Нам потрібно лише показати, що він не зациклюється на локальному мінімумі.

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

A[i]A[j]i<jA[i]>A[j]iji+1<jA[i]<A[i+1] and A[i+1]<A[j], but then A[i]<A[j] and we have a contradiction.

This proves that the algorithm only stops when the list is sorted. And hence we're done.


The techniques explained are so general that they virtually have nothing particular about greedy algorithm, the topic of this question.
Apass.Jack
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.