Як описати алгоритми, довести та проаналізувати їх?


20

Перш ніж читати «Мистецтво комп’ютерного програмування» (TAOCP) , я глибоко не розглядав ці питання. Я б використовував псевдо-код для опису алгоритмів, розуміння їх та оцінки часу виконання лише щодо порядків зростання. TAOCP ретельно змінює свою думку.

TAOCP використовує англійську мову, змішану з кроками та goto, для опису алгоритму, а також використовує діаграми потоків для більш легкого зображення алгоритму. Це здається низьким, але я вважаю, що є деякі переваги, особливо з блок-схемою, яку я багато ігнорував. Ми можемо позначити кожну зі стрілок твердженням про поточний стан справ у момент, коли обчислення проходить цю стрілку, і зробити індуктивний доказ алгоритму. Автор каже:

Автор стверджує, що ми дійсно розуміємо, чому алгоритм дійсний лише тоді, коли ми досягнемо того, що наш розум неявно заповнив усі твердження, як це було зроблено на рис.4.

Я такого не відчував. Ще одна перевага полягає в тому, що ми можемо порахувати кількість разів, які виконують кожен крок. Це легко перевірити за допомогою першого закону Кірхгофа. Я точно не проаналізував час роботи, тому деякі можливо, були опущені, коли я оцінював час роботи.±1

Аналіз порядків зростання іноді марний. Наприклад, ми не можемо відрізнити quicksort від гипсорта, оскільки всі вони є , де - очікуване число випадкової величини , тому ми повинні проаналізувати постійну, скажімо, і , таким чином ми можемо порівняти і краще. А також, іноді нам слід порівнювати інші кількості, наприклад, дисперсії. Недостатньо лише грубого аналізу порядків зростання часу роботи. Як TAOCPЕ(Т(н))=Θ(нжурналн)ЕХХЕ(Т1(н))=А1нlgн+Б1н+О(журналн)T 1 T 2Е(Т2(н))=А2lgн+Б2н+О(журналн)Т1Т2 переводить алгоритми в мову складання і обчислює час роботи. Мені це занадто важко, тому я хочу знати деякі методи аналізу трохи більш грубого часу, що також корисно для мов вищого рівня, таких як C, C ++ або псевдокоди.

І я хочу знати, який стиль опису використовується в основному в науково-дослідних роботах та як лікувати ці проблеми.


6
Ви повинні бути дуже обережними, порівнюючи час виконання алгоритмів. На справжніх комп’ютерах є кеші, регістри та трубопроводи, що може кардинально змінити час роботи. Якщо ви хочете з’ясувати, який алгоритм насправді швидший, вам потрібно реально запустити його на комп’ютері.
svick

1
Насправді, аналізувати асемблер, такий як Knuth, використовується набагато простіше, ніж аналіз коду реального життя, оскільки нічого не приховано, а керувати потоком легко. Ви просите практику; Я думаю , що коментар Дейва стосується. Практикуючі люди швидше розробляють свої алгоритми, використовуючи вимірювання часу виконання, ніж робити жорсткий аналіз. Але тоді я не практикуючий, тому прийміть те, що я кажу, із зерном солі.
Рафаель

1
@Raphael Моє на практиці означає, що на практиці дослідницьких робіт , а не програмування .
Yai0Phah

@Frank, що ти маєш на увазі під дисперсією ? Мої тести на ефективність дають мені відхилення у часі.
edA-qa mort-ora-y

@Raphael, ваш перший пункт це вже не справді. Сучасні фішки переробляють порядок складання, роблять магазини / завантаження поза порядком, а також передбачувані роботи та завантаження. Для одночасності та попередніх питань фактично потрібен ретельний аналіз, але я це не роблю в формальній формі.
edA-qa mort-ora-y

Відповіді:


18

Існує величезна різноманітність можливих підходів. Що найкраще підходить, залежить від

  • що ви намагаєтесь показати,
  • скільки деталей ви хочете чи потребуєте.

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

Я наведу вам три приклади для відомого алгоритму Mergesort, який, сподіваємось, це ілюструє.

Високий рівень

Алгоритм Mergesort приймає список, розбиває його на дві (приблизно) однаково довгі частини, повторюється за цими частковими списками і об'єднує (сортував) результати так, щоб кінцевий результат був відсортований. У одиночному або порожньому списках він повертає вхід.

Цей алгоритм, очевидно, є правильним алгоритмом сортування. Розбиття списку та його об'єднання може бути реалізоване у часі , що дає нам повтор у найгіршому випадку виконання T ( n ) = 2 T ( nΘ(н). За теоремою Майстра це оцінюється заT(n)Θ(nlogn).Т(н)=2Т(н2)+Θ(н)Т(н)Θ(нжурналн)

Середній рівень

Алгоритм Mergesort задається наступним псевдокодом:

procedure mergesort(l : List) {
  if ( l.length < 2 ) {
    return l
  }

  left  = mergesort(l.take(l.length / 2)
  right = mergesort(l.drop(l.length / 2)
  result = []

  while ( left.length > 0 || right.length > 0 ) {
    if ( right.length == 0 || (left.length > 0 && left.head <= right.head) ) {
      result = left.head :: result
      left = left.tail
    }
    else {
      result = right.head :: result
      right = right.tail
    }
  }

  return result.reverse
}

Доводимо правильність за допомогою індукції. Для списків довжиною нульовою або одиничною алгоритм є тривіально правильним. Як гіпотеза про індукцію, припустимо, що вона mergesortвиконує правильно у списках довжини не більше для деякого довільного, але фіксованого природного n > 1 . Тепер нехай L - список довжини n + 1 . За індукційною гіпотезою та утримуйте (не зменшуючи) відсортовані версії першої респ. друга половина L після рекурсивних викликів. Тому цикл вибирає в кожній ітерації найменший ще не досліджений елемент і додає його ; таким чином , це не все більш відсортований список, що містить усі елементи знн>1Lн+1leftrightLwhileresultresultleftі right. Зворотна сторона - це не зменшувана сортована версія , яка є поверненим - і бажаним - результатом.L

Щодо часу виконання, порахуймо порівняння елементів та перерахуємо операції (які домінують під час виконання асимптотично). Списки довжиною менше двох не викликають жодного. Для списків довжиною ми маємо ті операції, викликані підготовкою входів для рекурсивних викликів, тих із самих рекурсивних викликів плюс циклу та однієї . Обидва рекурсивних параметра можна обчислити з не більше n операцій зі списком. Цикл виконується рівно п раз і кожній ітерації причини не більше одного елемента порівняння і рівно два списки операцій. Фінал може бути реалізований з використанням 2 nн>1whilereverseнwhileнreverse2ноперації зі списком - кожен елемент видаляється із вхідних даних та заноситься у вихідний список. Отже, кількість операцій відповідає наступному повторенню:

Т(0)=Т(1)=0Т(н)Т(н2)+Т(н2)+7н

Оскільки явно не зменшується, для асимптотичного зростання достатньо врахувати n = 2 k . У цьому випадку рецидив спрощується доТн=2к

Т(0)=Т(1)=0Т(н)2Т(н2)+7н

За теоремою Мастера отримуємо який поширюється на час виконання .ТΘ(нжурналн)mergesort

Наднизький рівень

Розглянемо цю (узагальнену) реалізацію Mergesort в Ізабелі / HOL :

types dataset  =  "nat * string"

fun leq :: "dataset \<Rightarrow> dataset \<Rightarrow> bool" where
   "leq (kx::nat, dx) (ky, dy) = (kx \<le> ky)"

fun merge :: "dataset list \<Rightarrow> dataset list \<Rightarrow> dataset list" where
"merge [] b = b" |
"merge a [] = a" |
"merge (a # as) (b # bs) = (if leq a b then a # merge as (b # bs) else b # merge (a # as) bs)"

function (sequential) msort :: "dataset list \<Rightarrow> dataset list" where
  "msort []  = []" |
  "msort [x] = [x]" |
  "msort l   = (let mid = length l div 2 in merge (msort (take mid l)) (msort (drop mid l)))"
by pat_completeness auto
  termination
  apply (relation "measure length")
by simp+

Це вже включає докази чітко визначеності та припинення. Знайти (майже) повний доказ коректності тут .

Для "часу виконання", тобто кількості порівнянь, можна встановити повторення, подібне до попереднього розділу. Замість використання теореми Мастера та забуття констант можна також проаналізувати її, щоб отримати наближення, яке асимптотично дорівнює справжній величині. Повний аналіз можна знайти в [1]; ось приблизний контур (він не обов'язково відповідає коду Ізабель / HOL):

Як і вище, повторність для кількості порівнянь є

f0=f1=0fn=fn2+fn2+eн

eнн

{f2м=2fм+е2мf2м+1=fм+fм+1+е2м+1

fнен

к=1н-1(н-к)Δfк=fн-нf1

Δfк

W(с)=к1Δfкк-с=11-2-ск1Δеккс=: (с)

що разом з формулою Перрона веде нас до

fн=нf1+н2πi3-i3+i(с)нс(1-2-с)с(с+1)гс

(с)

fннжурнал2(н)+нА(журнал2(н))+1

А[-1,-0,9]


  1. Меллінові перетворення та асимптотика: повторення злиття Флайолета та Голіна (1992)
  2. ен=н2
    ен=н-1
    ен=н-н2н2+1-н2н2+1

αβТ(н)=Т(н/2)+Т(н/2)+αн+β

@Frank: коротка відповідь - Ви не можете ; константи залежать від деталей реалізації - включаючи архітектуру машини, мову та компілятор - які не мають значення для основного алгоритму.
JeffE

αβ

@JeffE, наприклад, MIX / MMIX в taocp є, але перекласти алгоритм на таку машинну мову занадто важко.
Yai0Phah

αβ

3

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

Починаючи подібну книгу, відразу стикається з питанням: "Якою мовою програмування я буду користуватися?", А це непросто питання презентації! Найважливішим, але й найбільш невловимим аспектом будь-якого інструменту є його вплив на звички тих, хто навчає себе у його використанні. Якщо інструмент є мовою програмування, цей вплив - це нам подобається чи ні - вплив на наші мислячі звички. Проаналізувавши цей вплив, наскільки мені було відомо, я прийшов до висновку, що жодна із існуючих мов програмування, а також їх підмножина не відповідає моєму призначенню; з іншого боку, я знав себе настільки не готовим до створення нової мови програмування, що я дав обітницю не робити цього протягом наступних п’яти років, і у мене було найбільш чітке відчуття, що цей період ще не минув! (До цього, серед багатьох інших, ця монографія мала бути написана.

Пізніше він пояснює, наскільки маленьким він зумів отримати свою міні-мову.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.