Як цей алгоритм сортування Θ (n³), а не Θ (n²), в гіршому випадку?


52

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

void F3() {
    for (int i = 1; i < n; i++) {
        if (A[i-1] > A[i]) {
            swap(i-1, i)
            i = 0
        }
    }
}

Це може бути не зрозуміло, але тут - розмір масиву, який ми намагаємося сортувати.nA

У будь-якому випадку, асистент викладача пояснив класу, що цей алгоритм працює в час (найгірший випадок, я вважаю), але незалежно від того, скільки разів я проходжу його через реверсивно відсортований масив, мені здається, що це має бути а не .Θ ( n 2 ) Θ ( n 3 )Θ(n3)Θ(n2)Θ(n3)

Хтось міг би мені пояснити, чому це Θ(n3) а не Θ(n2) ?


Вас може зацікавити структурований підхід до аналізу ; спробуйте самі знайти доказ!
Рафаель

Просто втіліть його в життя і виміряйте, щоб переконати себе. Масив із 10000 елементів у зворотному порядку повинен зайняти багато хвилин, а масив із 20 000 елементів у зворотному порядку повинен зайняти приблизно вісім разів довше.
gnasher729

@ gnasher729 Ви не помиляєтеся, але моє рішення інше: якщо ви спробуєте довести свій пов'язаний, ви незмінно зазнаєте невдачі, що скаже вам щось не так. (Звичайно, можна зробити і те, і інше. Розробка / підгонка, безумовно, швидша для відхилення гіпотези, але менш надійна . Поки ти робиш якийсь офіційний / структурований аналіз, не буде завдано ніякої шкоди. Покладаючись на сюжети - це там, де починаються проблеми.)O(n2)
Рафаель

1
через i = 0заяву
njzk2

Відповіді:


60

Цей алгоритм можна переписати так

  1. Скануйте, Aпоки не знайдете інверсію .
  2. Якщо ви знайдете його, поміняйте місцями і починайте спочатку
  3. Якщо такого немає, припиніть.

Тепер може бути не більше інверсій, і вам потрібно сканувати лінійний час, щоб знайти кожну, тож найгірший час роботи . Прекрасний приклад навчання, коли він підкреслює підхід, що відповідає шаблону, багато хто піддається!Θ(n(n2)Θ(n2)Θ(n3)

Nota bene: Треба бути трохи обережним: деякі інверсії з’являються рано, а інші пізно, тому не є суттєвим, що витрати складаються як заявлено (для нижньої межі). Вам також потрібно зауважити, що свопи ніколи не вводять нових інверсій. Більш детальний аналіз випадку із зворотно відсортованим масивом дасть щось подібне до квадратичного випадку формули Гаусса.

Як влучно зауважує @ gnasher729, легко помітити, що найгірший час роботи - , аналізуючи час роботи при сортуванні вхідних даних (хоча цей вхід, ймовірно , НЕ найгірший випадок).[ 1 , 2 , , n , 2 n , 2 n - 1 , , n + 1 ]Ω(n3)[1,2,,n,2n,2n1,,n+1]

Будьте уважні: не вважайте, що зворотно відсортований масив обов’язково буде вхідним для всіх алгоритмів сортування. Це залежить від алгоритму. Існують деякі алгоритми сортування, де реверсивно відсортований масив - не найгірший випадок, і навіть може бути близьким до найкращого випадку.


14
Якщо взяти масив, де перша половина складається з чисел 1 до n / 2 у порядку зростання, а друга половина - n до n / 2 + 1 у зворотному порядку, то очевидно, що вам потрібно принаймні n / 2 кроки, щоб знайти кожну інверсію, і буде приблизно (n / 2) ^ 2/2 з них. І це, швидше за все, не найгірший випадок.
gnasher729

@AnthonyRossello Це стандартний результат (у комбінаториці перестановок). Коротше кажучи, порахуйте кількість інверсій у зворотно відсортованому масиві (очевидно, що це найгірший випадок?); це сума Гаусса.
Рафаель

Треба пам’ятати, що незалежно від того, що часткові суми завжди є , це просто коефіцієнт, який швидко падає: (відзначимо досить великий коефіцієнт ). Біда в тому, що не переймається коефіцієнтами. Θ ( n α + 1 ) n k = 0Θ(nα)Θ(nα+1)1k=0nkα1α+1nα+11α+1Θ
йо

2
@yo 'І це стосується відповіді (або питання) як?
Рафаель

7

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

Зокрема, зауважте, що коли iвстановлюється нове максимальне значення, назвемо його N, масив [A[0], ..., A[N-1]]сортується у порядку зростання.

Отже, що відбувається, коли ми додаємо елемент A[N]до суміші?

Математика:

Ну, давайте скажемо, що він підходить у позиції . Тоді нам потрібні ітерацій циклу (які я позначу ), щоб перемістити його на місце , ітерацій, щоб перемістити його на місце , і взагалі: N кроків N - 1 N + ( N - 1 ) N - 2pNNstepsN1N+(N1)N2

stepsN(pN)=N+(N1)+(N2)++(pN+1)=12(N(N+1)pN(pN+1))

Для випадково відсортованого масиву приймає рівномірний розподіл на для кожного , з: { 0 , 1 , , N } NpN{0,1,,N}N

E(stepsN(pN))=a=1NP(pN=a)stepsN(a)=a=1N1N12(N(N+1)a(a+1))=12(N(N+1)13(N+1)(N+2))=13(N21)=Θ(N2)

сума може бути показана за допомогою формули Фолхабера або посилання Вольфрама Альфа внизу.

Для обернено відсортованого масиву, для всіх , і отримуємо:NpN=0N

stepsN(pN)=12N(N+1)

точно, приймаючи строго довше, ніж будь-яке інше значення .pN

Для вже відсортованого масиву та , при цьому умови нижчого порядку стають актуальними.кроків N ( p N ) = 0pN=NstepsN(pN)=0

Загальний час:

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

І знову, використовуючи лінійність очікування та формулу Фолхабера:

Expected Total Steps=E(N=1nstepsN(pN))=N=1nE(stepsN(pN))=Θ(n3)

Звичайно, якщо з якоїсь причини не є (наприклад, розподіл масивів, які ми дивимося, вже дуже близькі до сортування), то це не завжди потрібно бути справа. Але для досягнення цього потрібні дуже конкретні розподіли на !Θ ( N 2 ) p NstepsN(pN)Θ(N2)pN

Відповідне читання:


@Raphael - дякую за запропоновані вдосконалення, я додав трохи більше деталей. Ну випадкові змінні - це (від , набір впорядковань ), тому технічні очікування зроблено над Ω ΩpiΩAΩ
David E

Різні ; Я мав на увазі Ландау. Ω
Рафаель

3

Відмова:

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

незалежно від того, скільки разів я проходжу його через реверсивно відсортований масив, мені здається, що він повинен бути а не .Θ ( nΘ(n2)Θ(n3)

З таким простим кодом різницю між та не повинно бути важко помітити, і у багатьох практичних випадках це корисний підхід для перевірки нахилів або коригування очікувань.Θ ( n 3)Θ(n2)Θ(n3)


@Raphael вже відповів на ваше запитання, але тільки для ударів, встановивши вихід цієї програми на за допомогою цього скрипту gnuplot, повідомив про значення експонентів та та створив наступні графіки ( перший - нормальна шкала, а другий - масштаб журналу журналу):2.99796166833222 2.99223727692339f(x)=axb+cx2.997961668332222.99223727692339

нормальний лог

Сподіваюся, це допоможе¨


2
Ви можете підігнати будь-яку функцію до цих значень. Дивіться також тут .
Рафаель

3
@Raphael Якщо ви не хочете, щоб нітрип таким чином, тоді ні, ви не можете підходити до жодної функції (наприклад, ви не зможете пристосувати постійну функцію до будь-якої розумної точності). Це не є доказом, але вже є відповідь, яка дає ескіз. Щодо корисності, я можу процитувати ваш власний пост, який ви пов’язали: "Я повинен погодитися, що це дуже корисний підхід, який навіть іноді недостатньо використовується". Більше того, ОП заявив, що вважає, що це має бути а не , то чому б не експериментувати і перевірити, чи правильний його припущення? Проти. Θ ( n 3 )Θ(n2)Θ(n3)
dtldarek

2
Це свідчить про те, що алгоритм є але питання задає, чому . Це прохання пояснити явище, а не підтвердження його. Θ(n3)
Девід Річербі

2
@DavidRicherby Це означає, що ця відповідь не корисна?
dtldarek

3
@Magicsowon Це сайт із питаннями та відповідями, а не форум. Ми шукаємо відповіді на питання, а не обговорення навколо нього.
Девід Річербі

3

Припустимо, у вас є масив.

array a[10] = {10,8,9,6,7,4,5,2,3,0,1}

Ваш алгоритм робить наступне

Scan(1) - Swap (10,8) => {8,10,9,6,7,4,5,2,3,0,1}  //keep looking at "10"
Scan(2) - Swap (10,9) => {8,9,10,6,7,4,5,2,3,0,1}
...
Scan(10) - Swap(10,1) => {8,9,6,7,4,5,2,3,0,1,10}

В основному він переміщується до кінця масиву найвищим елементом, і при цьому він починає перевершуватись при кожному скануванні, ефективно роблячи O(n^2)рухи .. саме для цього одного елемента. Однак є п елементів, тому нам доведеться повторити цей nраз. Це не є офіційним доказом, але це допомагає зрозуміти "неформальним" способом час роботи O(n^3).


4
Що це додає до інших відповідей? Пояснення того, що робить алгоритм, вже було дано, а ваші міркування щодо виконання в кращому випадку є схематичними. (Найгірше не веде себе лінійно!)
Рафаель

2
Іноді є цінність пояснення однієї і тієї ж ідеї кількома способами (з формалізмом; простим прикладом "накачування інтуїції"), особливо коли людина, яка задає питання, новачка в цій галузі. Тож мені здається, що це додає, це те, що воно подано таким чином, що може допомогти інтуїції.
DW

Оскільки я отримав відповідь на свій коментар у прапорі (не робіть цього!): "Найгірший випадок не веде себе лінійно!" - Я маю на увазі алгебраїчні властивості оператора в гіршому випадку. Грубо кажучи, ви використовуєте WorstCase (1 + ... + n) "=" WorstCase (1) + ... + WorstCase (n), але ця ідентичність не відповідає.
Рафаель

1
Я новачок у цій галузі, і пояснення конкретним, прописаним прикладом безумовно допомогло мені зрозуміти проблему. Тепер прийняте рішення має для мене більше сенсу.
vaer-k

0

Логіка, схоже, сортує елементи масиву у порядку зростання.

Припустимо, найменше число знаходиться в кінці масиву (a [n]). Щоб він прийшов у потрібне місце - потрібні (n + (n-1) + (n-2) + ... 3 + 2 + 1) операції. = O (n2).

Для одного елемента в масиві O (n2) потрібні ops. Отже, для n еменцій це O (n3).


5
Що це додає до інших відповідей? Пояснення того, що робить алгоритм, вже було дано, а ваші міркування щодо виконання в кращому випадку є схематичними. (Найгірше не веде себе лінійно!)
Рафаель

Чудове пояснення. Це забезпечує інший, більш інтуїтивний погляд на проблему, не пояснений в інших відповідях. (Не кажучи вже про дуже короткий і легкий для розуміння.)
2501

1
@ 2501 Ні, це неправильно Спробуйте використати цю "інтуїцію" в алгоритмі Дейкстри, і ви отримаєте квадратичне виконання (у кількості вузлів), що невірно.
Рафаель

@Raphael Ні, це правильно, як пояснено у відповіді. Це пояснення працює для цього алгоритму, а не для інших. Хоча це може бути неправильним для них, ця претензія не підтверджує, що вона неправильна для цього.
2501

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