QuickSort (A, scanDown 1,
high); p>
) p>
Обчислювальна складність «швидкої» сортування h2>
Загальний аналіз ефективності «швидкої» сортування
досить важкий. Буде краще показати її обчислювальну складність, підрахувавши
число порівнянь при деяких ідеальних припущеннях. Припустимо, що n - ступінь
двійки, n = 2k (k = log2n), а центральний елемент розташовується точно
посередині кожного списку і розбиває його на два підсписки приблизно однаковою
довжини. p>
При першому скануванні проводиться n-1 порівнянь. У
результаті створюються два підсписки розміром n/2. На наступній фазі обробка
кожного підсписки вимагає приблизно n/2 порівнянь. Загальне число порівнянь на цій
фазі дорівнює 2 (n/2) = n. На наступній фазі обробляються чотири підсписки, що
вимагає 4 (n/4) порівнянь, і т.д. Врешті-решт процес розбиття припиняється
після k проходів, коли вийшли підсписки містять по одному елементу.
Загальне число порівнянь приблизно дорівнює p>
n +
2 (n/2) + 4 (n/4) + ... + N (n/n) = n + n + ... + N = n * k = n * log2n p>
Для списку загального вигляду обчислювальна складність
«Швидкої» сортування дорівнює O (n log2 n). Ідеальний випадок, який ми тільки що
розглянули, фактично виникає тоді, коли масив вже відсортований за
зростанням. Тоді центральний елемент потрапляє точно в середину кожного
підсписки. p>
Якщо масив відсортований за спаданням, то на перший
проході центральний елемент виявляється на середині списку і змінюється
місцями з кожним елементом як у першому, так і в другому підсписки.
Результуючий список майже відсортований, алгоритм має складність порядку O (n
log2n). p>
p>
Рис.6 p>
Найгіршим сценарієм для «швидкої» сортування буде
той, при якому центральний елемент весь час потрапляє в одноелементні
подспісок, а всі інші елементи залишаються в другому підсписки. Це відбувається
тоді, коли центральним завжди є найменший елемент. Розглянемо
послідовність 3, 8, 1, 5, 9. p>
На першому проході проводиться n порівнянь, а більший
подспісок містить n-1 елементів. На наступному проході цей подспісок вимагає
n-1 порівнянь і дає подспісок з n-2 елементів і т.д. Загальне число порівнянь
одно: p>
n + n-1 + n-2 + ... + 2 = n (n +1)/2 - 1 p>
Складність найгіршого випадку дорівнює O (n2), тобто не краще,
ніж для сортувань вставками і вибором. Проте цей випадок є
патологічним і малоймовірний на практиці. Загалом, середня продуктивність
«Швидкої» сортування вище, ніж у всіх розглянутих нами сортувань. P>
Алгоритм QuickSort вибирається за основу в більшості
універсальних сортують утиліт. Якщо ви не можете змиритися з
продуктивністю найгіршого випадку, використовуйте пірамідальну сортування --
більш стійкий алгоритм, складність якого дорівнює O (n log2n) і залежить тільки
від розміру списку. p>
Порівняння алгоритмів сортування масивів
p>
Ми порівняли алгоритми сортування, випробувавши їх на
масивах, що містять 4000, 8000, 10000, 15000 і 20000 цілих чисел,
відповідно. Час виконання виміряна в тіках (1/60 частка секунди). Серед
всіхалгоритмів порядку O (n2) час сортування вставками відображає той факт, що
на i-му проході потрібно лише i/2 порівнянь. Цей алгоритм явно перевершує
всі інші сортування порядку O (n2). Зауважте, що найгіршу загальну
продуктивність демонструє сортування методом бульбашки. Результати
випробувань показані в таблиці 1 і на малюнку 7. p>
Для ілюстрації ефективності алгоритмів сортування в
екстремальних випадках використовуються масиви з 20000 елементів, впорядкованих
по зростанню і за спаданням. При сортування методом бульбашки і сортування
вставками виконується тільки один прохід масиву, впорядкованого за
зростанням, у той час як сортування за допомогою вибору залежить тільки від
розміру списку і виробляє 19999 проходів. Упорядкованість даних за спаданням
є найгіршим випадком для бульбашкової, обмінної та сортування вставками,
зате сортування вибором виконується, як звичайно. p>
n p>
Обмінна сортування p>
Сортування вибором p>
Бульбашкова сортування p>
Сортування вставками p>
4 000 p>
12.23 p>
17.30 p>
15.78 p>
5.67 p>
8 000 p>
49.95 p>
29.43 p>
64.03 p>
23.15 p>
10 000 p>
77.47 p>
46.02 p>
99.10 p>
35.43 p>
15 000 p>
173.97 p>
103.00 p>
223.28 p>
80.23 p>
20 000 p>
313.33 p>
185.05 p>
399.47 p>
143.67 p>
p>
Рис.7 Порівняння сортувань порядку O (n2) p>
n p>
Обмінна сортування p>
Сортування вибором p>
Бульбашкова сортування p>
Сортування вставками p>
8 000 (впорядкований по
зростанням) p>
185.27 p>
185.78 p>
0.03 p>
0.05 p>
8 000 (впорядкований по
спаданню) p>
526.17 p>
199.00 p>
584.67 p>
286.92 p>
У загальному випадку QuickSort є найшвидшим алгоритмом.
Завдяки своїй ефективності, що дорівнює O (n log2n), він явно перевершує будь-який
алгоритм порядку O (n2). Судячи з результатів випробувань, наведених в наступній
таблиці, він також швидше будь-який з сортувань порядку O (n log2n), розглянутих
нами в попередньому номері. Зверніть увагу, що ефективність «швидкої»
сортування становить O (n log2n) навіть в екстремальних випадках. Зате сортування
за допомогою пошукового дерева стає в цих випадках O (n2) складною, тому що
сформоване дерево є виродженим. p>
n p>
Турнірна сортування p>
Сортування за допомогою
дерева p>
Пірамідальна сортування p>
"Швидка"
сортування p>
4 000 p>
0.28 p>
0.32 p>
0.13 p>
0.07 p>
8 000 p>
0.63 p>
0.68 p>
0.28 p>
0.17 p>
10 000 p>
0.90 p>
0.92 p>
0.35 p>
0.22 p>
15 000 p>
1.30 p>
1.40 p>
0.58 p>
0.33 p>
20 000 p>
1.95 p>
1.88 p>
0.77 p>
0.47 p>
8 000 (впорядкований по
зростанням) p>
1.77 p>
262.27 p>
0.75 p>
0.23 p>
8 000 (впорядкований по
спаданню) p>
1.65 p>
275.70 p>
0.80 p>
0.28 p>
p>
Рис.8 Порівняння сортувань порядку O (n log2n) p>
Порівняння сортувань
p>
Ця програма здійснює порівняння алгоритмів
сортування даних, представлених на рисунках 7 і 8. Тут ми наводимо тільки
базову структуру програми. Хронометраж проводиться за допомогою функції
TickCount, що повертає число 1/60 часток секунди, що минули з моменту старту
програми. p>
# include p>
# include "arrsort.h" p>
// Перечіслімий тип,
що описує початковий стан масиву даних. p>
enum Ordering (randomorder, ascending, descending); p>
// Перечіслімий тип,
ідентифікує алгоритм сортування. p>
enum SortType p>
( p>
SortTypeBegin, p>
exchange = SortTypeBegin, p>
selection, p>
bubble, p>
insertion, p>
tournament, p>
tree, p>
heap, p>
quick, p>
SortTypeEnd = quick p>
); p>
// копіювати n-елементний масив y в масив x p>
void Copy (int * x, int * y, int n) p>
( p>
for (int i = 0; i
* x + + = * y ++; p>
) p>
// Загальна сортуються
функція, яка приймає вихідний масив p>
// із заданою
упорядкованістю елементів і застосовує вказаний p>
// алгоритм сортування. p>
void Sort (int a [], int n, SortType type, Ordering order) p>
( p>
long time; p>
cout << "Сортування" <
// Вивести тип впорядкованості. p>
switch (order) p>
( p>
case random: p>
cout << "елементів."; p>
break; p>
case ascending: p>
cout << "елементів,
упорядкованих за зростанням. "; p>
break; p>
case descending: p>
cout << "елементів,
упорядкованих за спаданням. "; p>
break; p>
) p>
// засікти час p>
time = TickCount (); p>
// Виконати сортування і вивести її тип ... p>
switch (type) p>
( p>
case exchange: p>
ExchangeSort (a, n); p>
cout << "Сортування обміном:"; p>
break; p>
case selection: p>
SelectionSort (a, n); p>
cout << "Сортування вибором:"; p>
break; p>
case bubble:. . . . . . . p>
case insertion:. . . . . . . p>
case tournament:. . . . . . . p>
case tree:. . . . . . . p>
case heap:. . . . . . . p>
case quick:. . . . . . . p>
) p>
// Підрахувати час виконання у секундах. p>
time = TickCount () - time; p>
cout <
) p>
// Виконує сортування
масиву з n елементами, p>
// розташованих в порядку,
що визначається параметром order. p>
void RunTest (int n, Ordering order) p>
( p>
int i; p>
int * a, * b; p>
SortType stype; p>
RandomNumber rnd; p>
// Виділити пам'ять для двох n-елементних
масивів a і b. p>
a = new int [n]; p>
b = new int [n]; p>
// Визначити тип впорядкованості даних. p>
if (order == randomorder) p>
( p>
// Заповнити масив b випадковими числами. p>
for (i = 0; i
b [i] = rnd.Random (n); p>
) p>
else p>
( p>
// Дані, відсортовані за
зростанням. p>
for (i = 0; i
b [i] = i; p>
) p>
else p>
( p>
// дані, відсортовані за спаданням. p>
for (i = 0; i
b [i] = n - 1 - i; p>
) p>
else p>
( p>
// Копіювати дані в масив a.
По черзі виконати сортування для p>
// кожного типу. p>
for (stype = SortTypeBegin;
stype <= SortTypeEnd; stype = SortType (stype +1)) p>
( p>
Copy (a, b, n); p>
Sort (a, n, stype, order); p>
) p>
) p>
// Видалити обидва динамічних масиву. p>
delete [] a; p>
delete [] b; p>
) p>
// Відсортувати 4 000 -, 8
000 -, 10 000 -, 15 000 - і 20 000-елементний масиви, p>
// заповнені випадковими
числами. p>
// Потім спочатку
відсортувати 20 000-елементний масив, упорядкований p>
// за зростанням, а потім
за спаданням. p>
void main (void) p>
( p>
int nelts [5] = (4000, 8000,
10000, 15000, 20000); p>
int = i; p>
cout.precision (3); p>
cout.setf (ios:: fixed |
ios:: showpoint); p>
for (i = 0; i <5; i ++) p>
RunTest (nelts [i],
randomorder); p>
RunTest (20000, ascending); p>
RunTest (20000, descending); p>
Список літератури h2>
Для підготовки даної роботи були використані
матеріали з сайту http://www.rsdn.ru/
p>