Алгоритм сортування сегментів (Java, Python, C/C++ Приклади коду)
Що таке Bucket Sort?
Букетне сортування, яке часто називають bin-сортуванням, — це метод порівняльного сортування, який приймає невідсортований масив як вхідні дані та створює в результаті відсортований масив. Цей метод працює шляхом розподілу елементів у кілька сегментів і сортування кожного з цих блоків окремо за допомогою будь-якого алгоритму сортування, наприклад сортування вставкою. Потім усі сегменти об’єднуються, щоб утворити відсортований масив.
Сортування ковша зазвичай використовується, коли елементи:
- Значення з плаваючою комою
- Рівномірно розподілені по діапазону
Часова складність сортування сегментів залежить від кількості використаних сегментів і рівномірності розподілу вхідних даних. Хоча різні алгоритми сортування, наприклад сортування оболонки, сортування злиттям, сортування в купі та швидкий може досягти найкращої часової складності O(n*logn), алгоритм сортування ковша може досягти того ж самого за лінійною часовою складністю або O(n).
Сортування по відрах дотримується підходу розсіювання. Застосовуючи цей підхід, елементи розкидаються у відповідні відра, сортуються у відрах і збираються, щоб сформувати відсортований масив на останньому етапі. Цей підхід розсіяного збору обговорюється в наступному розділі
Розкид-Збір-Підхід
Масштабні, складні проблеми іноді можуть бути складними для вирішення. Підхід розсіяного збору намагається вирішити такі проблеми шляхом поділу всього набору даних на кластери. Потім кожен кластер розглядається окремо, і все повертається разом, щоб отримати остаточну відповідь.
Ось як алгоритм ковшового сортування реалізує метод розсіювання:
Як працює ковшове сортування
Основний принцип роботи ковшового сортування полягає в наступному:
- Створюється набір порожніх відер. Залежно від різних політик кількість сегментів може відрізнятися.
- З вхідного масиву помістіть кожен елемент у відповідне відро.
- Розсортуйте ці відра окремо.
- Об’єднайте відсортовані сегменти, щоб створити єдиний вихідний масив.
Детальні кроки роботи наведені в наступних розділах.
Псевдокодекс
Start Create N empty buckets For each array element: Calculate bucket index Put that element into the corresponding bucket For each bucket: Sort elements within each bucket Merge all the elements from each bucket Output the sorted array End
Спосіб 1: Алгоритм ковшового сортування для числа з плаваючою комою Numbers
Алгоритм ковшового сортування чисел з плаваючою комою в діапазоні від 0.0 до 1.0:
Крок 1) Створіть десять (10) порожніх відер так, щоб перше відро містило числа в діапазоні [0.0, 0.1). Тоді друге відро міститиме в межах [0.1, 0.2) і так далі.
Крок 2) Для кожного елемента масиву:
-
a. Обчисліть індекс сегмента за формулою:
індекс відра= кількість_відер *елемент_масиву
-
b. Вставте елемент у сегмент [bucket_index]
Крок 3) Сортуйте кожне відро окремо за допомогою сортування вставкою.
Крок 4) Об’єднайте всі сегменти в один масив.
Давайте подивимося на приклад сортування відра. У цьому прикладі ми відсортуємо наступний масив за допомогою алгоритму сортування в сегменті-
Крок 1) Спочатку ми створимо 10 порожніх відер. Перше відро міститиме числа між [0.0, 0.1). Тоді друге відро міститиме числа між [0.1, 0.2) і так далі.
Крок 2) Для кожного елемента масиву ми обчислимо індекс відра та розмістимо елемент у цьому відрі.
Індекс ковша можна розрахувати за формулою:
bucket_index= кількість_відер*елемент_масиву
Розрахунок індексу сегмента:
а) 0.78
bucket_index = no_of_buckets*array_element
= 10 * 0.78
= 7.8
Отже, елемент 0.78 зберігатиметься на bucket[floor(7.8)] або bucket[7].
b) 0.17
bucket_index = no_of_buckets * елемент масиву
= 10 * 0.17
= 1.7
Елемент масиву 0.17 зберігатиметься на bucket[floor(1.7)] або bucket[1].
с) 0.39
bucket_index = no_of_buckets * елемент масиву
= 10 * 0.39
= 3.9
0.39 буде зберігатися на bucket[floor(3.9)] або bucket[3].
Після повторення всіх елементів масиву відра будуть такими:
Крок 3) Потім кожне відро буде відсортовано за допомогою сортування вставкою. Після операції сортування результатом буде:
Крок 4) На останньому етапі сегменти будуть об’єднані в один масив. Цей масив буде відсортованим результатом введення.
Кожне відро буде об’єднано з вихідним масивом. Наприклад, конкатенація елементів другого сегмента:
Конкатенація останніх елементів відра буде наступною:
Після конкатенації отриманий масив буде бажаним відсортованим масивом.
Програма сортування сегментів на C/C++
Вхідний сигнал:
//Bucket Sort Program in C/C++ //For not having integer parts #include <bits/stdc++.h> #define BUCKET_SIZE 10 using namespace std; void bucketSort(float input[], int array_size) { vector <float>bucket[BUCKET_SIZE]; for (int i = 0; i < array_size; i++) { int index = BUCKET_SIZE*input[i]; bucket[index].push_back(input[i]); } for (int i = 0; i < BUCKET_SIZE; i++) sort(bucket[i].begin(), bucket[i].end()); int out_index = 0; for (int i = 0; i < BUCKET_SIZE; i++) for (int j = 0; j < bucket[i].size(); j++) input[out_index++] = bucket[i][j]; } int main() { float input[]={0.78,0.17,0.39,0.26,0.72,0.94,0.21,0.12,0.23,0.69}; int array_size = sizeof(input)/sizeof(input[0]); bucketSort(input, array_size); cout <<"Sorted Output: \n"; for (int i = 0; i< array_size; i++) cout<<input[i]<<" "; return 0; }
вихід:
Sorted Output: 0.12 0.17 0.21 0.23 0.26 0.39 0.69 0.72 0.78 0.94
Bucket Sort Program in Python
Вхідний сигнал:
# Bucket Sort Program in Python # For not having integer parts def bucketSort(input): output = [] bucket_size = 10 for bucket in range(bucket_size): output.append([]) for element in input: index = int(bucket_size * element) output[index].append(element) for bucket in range(bucket_size): output[bucket] = sorted(output[bucket]) out_index = 0 for bucket in range(bucket_size): for element in range(len(output[bucket])): input[out_index] = output[bucket][element] out_index += 1 return input input = [0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12, 0.23, 0.69] print("Sorted Output:") print(bucketSort(input))
вихід:
Sorted Output: [0.12, 0.17, 0.21, 0.23, 0.26, 0.39, 0.69, 0.72, 0.78, 0.94]
Відро Сортувати в Java
Вхідний сигнал:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class BucketSort { private static final int BUCKET_SIZE = 10; public static void bucketSort(float[] input, int arraySize) { List < Float > [] bucket = new ArrayList[BUCKET_SIZE]; for (int i = 0; i < arraySize; i++) { int index = (int)(BUCKET_SIZE * input[i]); if (bucket[index] == null) { bucket[index] = new ArrayList < > (); } bucket[index].add(input[i]); } for (int i = 0; i < BUCKET_SIZE; i++) { if (bucket[i] != null) { Collections.sort(bucket[i]); } } int outIndex = 0; for (int i = 0; i < BUCKET_SIZE; i++) { if (bucket[i] != null) { for (float value: bucket[i]) { input[outIndex++] = value; } } } } public static void main(String[] args) { float[] input = {0.78f,0.17f,0.39f,0.26f,0.72f,0.94f,0.21f,0.12f,0.23f,0.69f}; int arraySize = input.length; bucketSort(input, arraySize); System.out.println("Sorted Output:"); for (int i = 0; i < arraySize; i++) { System.out.print(input[i]+" "); } } }
вихід:
Sorted Output: 0.12 0.17 0.21 0.23 0.26 0.39 0.69 0.72 0.78 0.94
Спосіб 2: Алгоритм ковшового сортування для цілих елементів
Алгоритм сортування сегментів для вхідних даних, які містять числа за межами діапазону [0.0, 1.0], дещо відрізняється від попереднього алгоритм. У цьому випадку необхідно виконати наступні кроки:
Крок 1) Знайдіть максимальний і мінімальний елементи.
Крок 2) Виберіть кількість відер, n, і ініціалізуйте ці відра як порожні.
Крок 3) Обчисліть діапазон або проміжок кожного відра за формулою:
span = (maximum - minimum)/n
Крок 4) Для кожного елемента масиву:
-
1. Розрахувати індекс сегмента:
bucket_index = (element - minimum)/span
-
2. Вставте елемент у відро [bucket_index]
Крок 5) Відсортуйте кожне відро за допомогою сортування вставкою.
Крок 6) Об’єднайте всі сегменти в один масив.
Давайте розглянемо приклад цього алгоритму ковшового сортування. У цьому прикладі ми відсортуємо наступний масив за допомогою алгоритму сортування в сегменті-
Крок 1) На першому кроці необхідно знайти максимальний і мінімальний елементи заданого масиву. Для цього прикладу максимум становить 24, а мінімум – 1.
Крок 2) Тепер нам потрібно вибрати кількість порожніх відер, n. У цьому прикладі ми візьмемо 5 відер. Тоді ми ініціалізуємо їх як порожні.
Крок 3) Проліт кожного ковша необхідно розрахувати за такою формулою:
span = (maximum-minimum)/n = (24-1)/5 = 4
;
Отже, перше відро міститиме числа в діапазоні [0, 5). Друге відро міститиме числа в межах [5, 10) і так далі.
Крок 4) Для кожного елемента масиву ми обчислимо індекс відра та розмістимо елемент у цьому відрі. Індекс ковша можна розрахувати за формулою:
bucket_index = (element - minimum)/span
Розрахунок індексу сегмента:
-
a) 11bucket_index = (елемент – мінімум)/span
=(11-1)/4
=2
Таким чином, елемент 11 буде зберігатися в bucket[2].
-
b) 9
bucket_index = (елемент – мінімум)/span
=(9-1)/4
=2
Примітка: Оскільки 9 є граничним елементом для bucket[1], його потрібно додати в bucket[1] замість додавання в те саме відро попереднього елемента.
Після виконання операцій для кожного елемента відра будуть такими:
Крок 5) Тепер кожне відро буде відсортовано за допомогою сортування вставкою. Відра після сортування-
Крок 6) На останньому етапі сегменти будуть об’єднані в один масив. що масив буде відсортованим результатом введення.
Програма сортування сегментів на C/C++
Вхідний сигнал:
#include<bits/stdc++.h> using namespace std; void bucketSort(vector < double > & input, int No_Of_Buckets) { double max_value = * max_element(input.begin(), input.end()); double min_value = * min_element(input.begin(), input.end()); double span = (max_value - min_value) / No_Of_Buckets; vector<vector <double>> output; for (int i = 0; i < No_Of_Buckets; i++) output.push_back(vector <double> ()); for (int i = 0; i < input.size(); i++) { double difference = (input[i] - min_value) / span - int((input[i] - min_value) / span); if (difference == 0 && input[i] != min_value) output[int((input[i] - min_value) / span) - 1] .push_back(input[i]); else output[int((input[i] - min_value) / span)].push_back( input[i]); } for (int i = 0; i < output.size(); i++) { if (!output[i].empty()) sort(output[i].begin(), output[i].end()); } int index = 0; for (vector <double> & bucket: output) { if (!bucket.empty()) { for (double i: bucket) { input[index] = i; index++; } } } } int main() { vector <double> input ={11,9,21,8,17,19,13,1,24,12 }; int No_Of_Buckets = 5; bucketSort(input, No_Of_Buckets); cout<< "Sorted Output:"; for (int i; i < input.size(); i++) cout <<input[i]<<" "; return 0; }
вихід:
Sorted Output:1 8 9 11 12 13 17 19 21 24
Bucket Sort Program in Python
Вхідний сигнал:
def bucketSort(input, No_Of_Buckets): max_element = max(input) min_element = min(input) span = (max_element - min_element) / No_Of_Buckets output = [] for bucket in range(No_Of_Buckets): output.append([]) for element in range(len(input)): diff = (input[element] - min_element) / span - int( (input[element] - min_element) / span ) if diff == 0 and input[element] != min_element: output[int((input[element] - min_element) / span) - 1].append( input[element] ) else: output[int((input[element] - min_element) / span)].append(input[element]) for bucket in range(len(output)): if len(output[bucket]) != 0: output[bucket].sort() index = 0 for bucket in output: if bucket: for element in bucket: input[index] = element index = index + 1 input = [11, 9, 21, 8, 17, 19, 13, 1, 24, 12] No_Of_Buckets = 5 bucketSort(input, No_Of_Buckets) print("Sorted Output:\n", input)
вихід:
Sorted Output: [1, 8, 9, 11, 12, 13, 17, 19, 21, 24]
Відро Сортувати в Java
Вхідний сигнал:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class BucketSort { public static void bucketSort(List < Double > input, int No_Of_Buckets) { double max_value = Collections.max(input); double min_value = Collections.min(input); double span =(max_value - min_value) / No_Of_Buckets; List < List < Double > > output = new ArrayList < > (); for (int i = 0; i < No_Of_Buckets; i++) { output.add(new ArrayList < > ()); } for (Double value: input) { double difference = (value - min_value) / span - ((value - min_value) / span); if (difference == 0 && value != min_value) { output.get((int)((value - min_value) / span) - 1).add(value); } else { output.get((int)((value - min_value) / span)).add(value); } } for (List <Double> bucket: output) { if (!bucket.isEmpty()) { Collections.sort(bucket); } } int index = 0; for (List <Double> bucket: output) { if (!bucket.isEmpty()) { for (Double value: bucket) { input.set(index,value); index++; } } } } public static void main(String[] args) { List <Double> input = new ArrayList <> (); input.add(11.0); input.add(9.0); input.add(21.0); input.add(8.0); input.add(17.0); input.add(19.0); input.add(13.0); input.add(1.0); input.add(24.0); input.add(12.0); int No_Of_Buckets = 5; bucketSort(input, No_Of_Buckets); System.out.println("Sorted Output:"); for (Double value: input) { System.out.print(value + " "); } } }
вихід:
Sorted Output: 1.0 8.0 9.0 11.0 12.0 13.0 17.0 19.0 21.0 24.0
Плюси мінуси
Плюси | мінуси |
---|---|
Виконуйте швидші обчислення | Споживає більше місця порівняно з іншими алгоритмами |
Його можна використовувати як зовнішній метод сортування | Погано працює, якщо дані розподілені нерівномірно |
Відра можна обробити самостійно |
Аналіз складності ковшового сортування
Складність сортування за часом
- Найкраща складність випадку:Якщо всі елементи масиву рівномірно розподілені та відсортовані заздалегідь, знадобиться O(n) часу, щоб розкидати елементи у відповідні відра. Потім сортування кожного відра за допомогою сортування вставки коштуватиме O(k). Таким чином, загальна складність буде O(n+k).
- Середня складність справи:Для середніх випадків ми припускаємо, що входи рівномірно розподілені. Таким чином, алгоритм ковшового сортування досягає лінійної часової складності O(n+k). Тут O(n) часу потрібно для розсіювання елементів і O(k) часу потрібно для сортування цих елементів за допомогою сортування вставкою.
- Найгірша складність:У гіршому випадку елементи не будуть рівномірно розподілені та зосереджені в одному або двох конкретних відрах. У цьому випадку сортування з відром працюватиме як a алгоритм бульбашкового сортування. Отже, у гіршому випадку часова складність ковшового сортування становитиме O(n^2).
Просторова складність ковшового сортування
Складність простору ковшового сортування становить O(n*k). Тут n — кількість елементів, а k — необхідна кількість відер.