Open vldnik opened 5 years ago
Insertion sort работает быстрее чем merge sort в случае с небольшими массивами. В ниже приведенном примере я заметил, что при заполнении массива случайными числами, и используя threshold (в данном случае значение, ниже которого выполняется сортировка вставкой) в диапазоне 16 – 64 сортировка вставкой проявила себя примерно в два раза быстрее, чем merge sort.
Разница становится заметней при увеличении изначально заданного массива чисел в размере. В программе я реализовал гибридную сортировка. Ее нельзя назвать стабильной сортировкой, ведь порядок одинаковых элементов может не сохраниться из-за перестановки во время выполнения merge сортировки.
При использовании массива в 500 000 целых чисел сортировка занимает около 1/8 секунды. Для эксперимента я увеличил начальное значение до 16 миллионов целых чисел, в данном случае сортировка дала результат в чуть более 4 секунд. Без использования insertion sort, сортировка занимает около 4, 5 секунд, а с ней и установленным threshold в размере 60 сортировка занимает чуть менее 4 секунд, что составляет около 10% увеличении производительности.
В данном эксперименте гибридная сортировка показала, что используя квадратичные алгоритмы для сортировки подмассивов увеличивает производительность алгоритма. И чем больше массив по размеру, тем заметней увеличение производительности.
static final int threshold = 60; // Insertion sort при <= threshold
public static void main(String[] args) {
int[] unsortedArr = new int[16 * 1024 * 1024];
Random random = new Random();
for (int i = 0; i < unsortedArr.length; i++)
unsortedArr[i] = random.nextInt();
long start = System.currentTimeMillis();
mergeSort(unsortedArr, 0, unsortedArr.length);
long end = System.currentTimeMillis();
System.out.println("Время (мс): " + (end - start));
}
static void swap(int[] arr, int first, int last) {
int tmp = arr[first];
arr[first] = arr[last];
arr[last] = tmp;
}
static void merge(int[] arr, int a, int b, int c, int d, int e) {
while (a < b && c < d)
swap(arr, e++, arr[b] < arr[c] ? a++ : c++);
while (a < b)
swap(arr, e++, a++);
while (c < d)
swap(arr, e++, c++);
}
static void mainSort(int[] a, int b, int e, int w) {
int m;
if (e - b > 1) {
m = b + (e - b) / 2;
mergeSort(a, b, m);
mergeSort(a, m, e);
merge(a, b, m, m, e, w);
} else {
while (b < e) {
swap(a, b++, w++);
}
}
}
static void mergeSort(int[] a, int b, int e) {
int m, n, w, x;
int t;
// Используем Insertion sort если <= threshold
if (e - b <= threshold) {
for(n = b+1; n < e; n++) {
t = a[n];
m = n-1;
while(m >= b && a[m] > t) {
a[m+1] = a[m];
m--;
}
a[m+1] = t;
}
return;
}
if (e - b > 1) {
m = b + (e - b) / 2;
w = b + e - m;
wsort(a, b, m, w);
while (w - b > 2) {
// w = опорная точка (середина)
n = w;
w = b + (n - b + 1) / 2;
x = b + n - w;
wsort(a, w, n, b);
merge(a, b, x, n, e, w);
}
for (n = w; n > b; --n) {
t = a[n-1];
for (m = n; m < e && a[m] < t; ++m)
a[m-1] = a[m];
a[m-1] = t;
}
}
}
MainSort | QuickSort | InsertionSort | HeapSort | |
---|---|---|---|---|
16 | 1046 | 368 | 3 | 1353 |
32 | 1198 | 460 | 7 | 1561 |
64 | 7155 | 576 | 25 | 2015 |
100 | 29403 | 3973 | 49 | 2097 |
1000 | 30785 | 1390 | 3254 | 3836 |
10000 | 41417 | 14559 | 410196 | 5909 |
20000 | 51313 | 29403 | 2619115 | 18815 |
50000 | 55735 | 56187 | 10412852 | 40084 |
80000 | 61916 | 84337 | 25013979 | 58720 |
Цей алгоритм поєднує відразу три різних види сортування, підібраних відповідно до розміру вхідного масиву. В його основі лежить алгоритм швидкого сортування. У разі, якщо розмір після розбиття вхідного масиву буде більше 16 елементів, тоді обирається сортування вставками. Як відомо, він добре працює на невеликих масивах та майже відсортованих, тому для елементів не більше 16, він прискорює загальний алгоритм. Найоптимальніший варіант 16-32 елементи. Якщо використовувати іншу кількість елементів наприклад більше 100, то сортування вставками буде працювати значно менш ефективно. Найкраще проявив себе, коли розмір був не більше 64 елементів. Далі його ефективність значно погіршується, що впливає на ефективність всього алгоритму в цілому. Якщо розмір перевищує (2log(n)) – де n – кількість елементів, тоді використовується Heap sort. Він забезпечує складність O(nlog(n)) в усіх випадках на відмінну від швидкого сортування, який в найгіршому випадку O(n^2). Якщо замість нього обрати merge sort, який також забезпечує таку складність, то буде використовуватись більше пам’яті. В такому випадку забезпечується ефективність відразу трьох алгоритмів, оскільки враховуються їхні найкращі результати. Після вимірювання часу виявилось, що цей алгоритм працює краще за швидке сортування для масивів значної довжини. Уже для понад 100000 він обігнав по швидкості швидке сортування. Щодо сортування вставками, то тут його застосовувати вже не доцільно, В таблиці наведена їх залежність від кількості елементів. Час позначено в секундах. Посилання на власну реалізацію: https://github.com/FairyFox5700/SortMethod/blob/master/IntroSort/Program.cs
Pretty interesting algorithm. The main idea of it we can see below:
Abstract
We present a new deterministic algorithm for the sparse Fourier transform problem, in which we seek to identify k ≪ N significant Fourier coefficients from a signal of bandwidth N. Previous deterministic algorithms exhibit quadratic runtime scaling, while our algorithm scales linearly with k in the average case. Underlying our algorithm are a few simple observations relating the Fourier coefficients of time-shifted samples to unshifted samples of the input function. This allows us to detect when aliasing between two or more frequencies has occurred, as well as to determine the value of unaliased frequencies. We show that empirically our algorithm is orders of magnitude faster than competing algorithms.
Merge sort has a reliable workload / speed regardless of data size or the 'sortedness' of the data. It has good worst-case behaviour, but you can find plenty of other algorithms with a better best-case behaviour. Bubble sort actually has the best possible best-case sort behaviour (for a sorted list), because if the list is already sorted then nothing is changed and you've only needed to iterate through the list once to find that out. If you want a "better" algorithm that is more comparable to bubble sort, then quick sort (or its variants) are a more appropriate comparison. Quick sort is sort of like bubble sort, but skips a lot of intermediate steps (you switch a value with another value without switching all the intermediate values between). However, quick sort performs fairly badly on a sorted list, and can take of the order of n^2 operations to 'sort' a sorted list of size n.
Sorting inserts works faster than sorting attachments in case of small arrays. At the bottom of the screen there was a case that it got in order (in this case, the value will be lower than selected) in the range 16-64 from the sorting.
The difference becomes appreciable when increasing the initial task. In the program I implemented hybrid sorting. It can not be called stable sorting, since the order of identical elements may not be retained due to permutations when performing a merge sort.
Adaptive due to sub-algorithms Адаптивність за рахунок під-алгоритмів
Implement a recursive O(n log n) sorting algorithm (e.g. quicksort or merge sort), and for small sub-array sizes use one of the O(n^2) algorithms (insertion, selection, bubble, ...) Check if this will speedup the overall algorithm, and by how much Experimentally select the optimal threshold values for the transition to a quadratic algorithm Consider adaptive and nonadaptive implementations of a quadratic algorithm and compare their work on those arrays where adaptive algorithms are more efficient. Determine the effect of the adaptivity of the quadratic sub-algorithm on the duration of the algorithm as a whole This task is more complex, but it is worth more points. Can be done in teams. You can implement various combinations of algorithms, in different programming languages, ...
Реалізувати рекурсивний алгоритм сортування quicksort або merge sort, причому для маленьких розмірів під-масивів використовувати один з квадратичних алгоритмів (insertion, selection, bubble, …) Перевірити, чи буде від цього прискорення загального алгоритму, і на скільки Підібрати експериментально оптимальні значення порогу для переходу до квадратичного алгоритму Розглянути адаптивні та неадаптивні реалізації квадратичного алгоритму і порівняти їх роботу для тих масивів, де адаптивність дає перевагу. Визначити, який вплив має адаптивність квадратичного під-алгоритму на час роботи алгоритму в цілому Це завдання є більш складним, але за нього буде більше балів. Можна виконувати в командах. Можна реалізовувати різні комбінації алгоритмів, на різних мовах програмування, …