Skip to content

Commit

Permalink
refs #148 Более качественные комментарии к сортировке подсчётом
Browse files Browse the repository at this point in the history
  • Loading branch information
izvolov committed Aug 18, 2021
1 parent cea2d08 commit 13eae20
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 141 deletions.
90 changes: 54 additions & 36 deletions include/burst/algorithm/counting_sort_copy_par.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,37 @@

namespace burst
{
//! Сортировка подсчётом.
/*!
Алгоритм целочисленной сортировки, работающий за линейное время и использующий
O(max(N, M)) дополнительной памяти, где N — размер входного диапазона, M — максимальное
значение сортируемых целых чисел. Максимальное значение вычисляется на этапе компиляции,
исходя из типа, возвращаемого отображением "Map".
В версии "_copy" результат сортировки записывается в выходной диапазон, а входной
диапазон остаётся без изменений.
В версии "_move" элементы входного диапазона в результате сортировки перемещаются в
выходной.
Выходной диапазон должен быть не меньше входного, чтобы в него влезли
отсортированные данные.
\tparam ForwardIterator
Тип принимаемого на вход диапазона, который нужно отсортировать. Для него достаточно
быть однонаправленным итератором.
\tparam RandomAccessIterator
Тип итератора выходного диапазона, в который будут записаны отсортированные данные.
Должен быть итератором произвольного доступа.
\tparam Map
Отображение входных значений в целые числа.
Сортировка происходит по значениям этого отображения. Поэтому от него требуется, чтобы
результатом отображения были целые числа.
Алгоритм работы.
1. Заводится массив счётчиков, по длине не уступающий максимальному значению, принимаемому
типом сортируемых чисел, поскольку индексы в массиве счётчиков соответствуют сортируемым
числам.
2. Входной диапазон при помощи заданного отображения переводится в набор целых чисел, и для
каждого из них подсчитывается количество вхождений в исходный диапазон.
3. Массив счётчиков преобразуется в кумулятивный вид, то есть к каждому (i + 1)-му счётчику
последовательно прибавляется значение i-го. Таким образом получается массив, в котором
на i-м месте лежит индекс в выходном массиве элемента, который отображается в число i.
4. Проходим по входному диапазону и, используя полученный массив индексов, записываем
элементы входного диапазона на их места в отсортированном диапазоне.
\brief
Параллельный вариант сортировки подсчётом
\details
Отличия от последовательного алгоритма:
Так же, как и последовательный алгоритм, работает за время `O(N)`, но использует больше
дополнительной памяти: `O(max(N, M * T))`, где `N` — размер входного диапазона, `M` —
максимальное значение, которое может принимать тип сортируемых чисел, `T` — количество
потоков, участвующих в сортировке.
Алгоритм работы:
1. Разбивает входной диапазон на логические куски, каждый из которых будет
обрабатываться в отдельном потоке.
2. В каждом куске собираются счётчики. У каждого потока свой массив счётчиков,
независимый от других потоков.
3. Когда все потоки собрали свои счётчики, все счётчики синхронизируются между кусками
так, чтобы каждый поток знал позиции именно своих элементов в итоговом массиве
(см. Harada, Howes, "Introduction to GPU Radix Sort"
http://www.heterogeneouscompute.org/wordpress/wp-content/uploads/2011/06/RadixSort.pdf)
4. Каждый поток распределяет элементы своего куска по результирующему диапазону.
\param par
Тег, указывающий на то, что нужно вызвать параллельный вариант алгоритма, и содержащий
желаемое количество потоков для параллелизации.
\param [first, last)
Диапазон, который нужно отсортировать. Должен быть диапазоном произвольного доступа.
\see counting_sort_copy_seq.hpp
\see parallel_policy
*/
template <typename RandomAccessIterator1, typename RandomAccessIterator2, typename Map>
RandomAccessIterator2
Expand Down Expand Up @@ -86,6 +81,14 @@ namespace burst
}
}

/*!
\brief
Параллельная сортировка подсчётом без пользовательского отображения
\details
В этом случае элементы берутся "как есть" то есть подсчитываются непосредственно
значения элементов, лежащих во входном диапазоне.
*/
template <typename RandomAccessIterator1, typename RandomAccessIterator2>
RandomAccessIterator2
counting_sort_copy
Expand All @@ -99,7 +102,13 @@ namespace burst
return counting_sort_copy(par, first, last, result, identity);
}

//! Параллельный диапазонный вариант сортировки подсчётом
/*!
\brief
Диапазонный вариант параллельной сортировки подсчётом
\details
Сортируемые данные задаются не парой итераторов, а сразу диапазоном.
*/
template <typename RandomAccessRange, typename RandomAccessIterator, typename Map>
RandomAccessIterator
counting_sort_copy
Expand All @@ -123,6 +132,15 @@ namespace burst
);
}

/*!
\brief
Диапазонный вариант параллельной сортировки подсчётом без пользовательского отображения
\details
Сортируемые данные задаются не парой итераторов, а сразу диапазоном, а элементы берутся
"как есть" то есть подсчитываются непосредственно значения элементов, лежащих во входном
диапазоне.
*/
template <typename RandomAccessRange, typename RandomAccessIterator>
RandomAccessIterator
counting_sort_copy
Expand Down
99 changes: 60 additions & 39 deletions include/burst/algorithm/counting_sort_copy_seq.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,48 @@

namespace burst
{
//! Сортировка подсчётом.
/*!
Алгоритм целочисленной сортировки, работающий за линейное время и использующий
O(max(N, M)) дополнительной памяти, где N — размер входного диапазона, M — максимальное
значение сортируемых целых чисел. Максимальное значение вычисляется на этапе компиляции,
исходя из типа, возвращаемого отображением "Map".
В версии "_copy" результат сортировки записывается в выходной диапазон, а входной
диапазон остаётся без изменений.
В версии "_move" элементы входного диапазона в результате сортировки перемещаются в
выходной.
Выходной диапазон должен быть не меньше входного, чтобы в него влезли
отсортированные данные.
\tparam ForwardIterator
Тип принимаемого на вход диапазона, который нужно отсортировать. Для него достаточно
быть однонаправленным итератором.
\tparam RandomAccessIterator
Тип итератора выходного диапазона, в который будут записаны отсортированные данные.
\brief
Классическая устойчивая сортировка подсчётом
\details
[Устойчивый алгоритм сортировки подсчётом](https://ru.wikipedia.org/wiki/Сортировка_подсчётом#Устойчивый_алгоритм)
Алгоритм целочисленной сортировки, работающий за время `O(N)` и использующий
`O(max(N, M))` дополнительной памяти, где `N = |[first, last)|` — размер входного
диапазона, `M = std::numeric_limits<decltype(map(*first))>::max()` — максимальное
значение, которое может принимать тип сортируемых чисел.
В версии "_copy" результат сортировки копируется в выходной диапазон, а входной диапазон
остаётся без изменений.
Алгоритм работы:
1. Заводится массив счётчиков размера `M`.
2. Входной диапазон при помощи отображения `map` переводится в набор целых чисел, и для
каждого из этих чисел подсчитывается количество вхождений.
3. Массив счётчиков преобразуется в кумулятивный вид, то есть к каждому `(i + 1)`-му
счётчику последовательно прибавляется значение `i`-го. Таким образом получается
массив, в котором на `i`-м месте лежит индекс в выходном массиве элемента, который
отображается в число `i`.
4. Используя полученный массив индексов, записываем элементы входного диапазона на их
места в отсортированном диапазоне.
\param [first, last)
Диапазон, который нужно отсортировать. Должен быть хотя бы однонаправленным диапазоном.
\param result
Итератор на начало выходного диапазона, в который будут записаны отсортированные данные.
Выходной диапазон, начало которого задаёт этот итератор, должен быть достаточного
размера (не меньше, чем `|[first, last)|`), чтобы в него был записан отсортированный
массив.
Должен быть итератором произвольного доступа.
\tparam Map
\param map
Отображение входных значений в целые числа.
Сортировка происходит по значениям этого отображения. Поэтому от него требуется, чтобы
результатом отображения были целые числа.
Алгоритм работы.
1. Заводится массив счётчиков, по длине не уступающий максимальному значению, принимаемому
типом сортируемых чисел, поскольку индексы в массиве счётчиков соответствуют сортируемым
числам.
2. Входной диапазон при помощи заданного отображения переводится в набор целых чисел, и для
каждого из них подсчитывается количество вхождений в исходный диапазон.
3. Массив счётчиков преобразуется в кумулятивный вид, то есть к каждому (i + 1)-му счётчику
последовательно прибавляется значение i-го. Таким образом получается массив, в котором
на i-м месте лежит индекс в выходном массиве элемента, который отображается в число i.
4. Проходим по входному диапазону и, используя полученный массив индексов, записываем
элементы входного диапазона на их места в отсортированном диапазоне.
результатом отображения были целые числа, причём лучшие результаты достигаются, как
правило, на однобайтовых числах.
\returns
Итератор за последним отсортированным элементом в выходном диапазоне.
*/
template <typename ForwardIterator, typename RandomAccessIterator, typename Map>
RandomAccessIterator
Expand All @@ -62,6 +68,14 @@ namespace burst
detail::counting_sort_impl(first, last, result, compose(to_unsigned, std::move(map)));
}

/*!
\brief
Перегрузка без пользовательского отображения
\details
В этом случае элементы берутся "как есть" то есть подсчитываются непосредственно
значения элементов, лежащих во входном диапазоне.
*/
template <typename ForwardIterator, typename RandomAccessIterator>
RandomAccessIterator
counting_sort_copy
Expand All @@ -74,18 +88,16 @@ namespace burst
return counting_sort_copy(first, last, result, identity);
}

//! Диапазонный вариант сортировки подсчётом
/*!
\brief
Диапазонный вариант сортировки подсчётом
\details
Отличается только тем, что вместо пары итераторов принимает диапазон.
*/
template <typename ForwardRange, typename RandomAccessIterator, typename Map>
RandomAccessIterator
counting_sort_copy
(
ForwardRange && range,
RandomAccessIterator result,
Map map
)
counting_sort_copy (ForwardRange && range, RandomAccessIterator result, Map map)
{
using std::begin;
using std::end;
Expand All @@ -99,6 +111,15 @@ namespace burst
);
}

/*!
\brief
Диапазонный вариант сортировки подсчётом без пользовательского отображения
\details
Сортируемые данные задаются не парой итераторов, а сразу диапазоном, а элементы берутся
"как есть" то есть подсчитываются непосредственно значения элементов, лежащих во входном
диапазоне.
*/
template <typename ForwardRange, typename RandomAccessIterator>
RandomAccessIterator counting_sort_copy (ForwardRange && range, RandomAccessIterator result)
{
Expand Down
Loading

0 comments on commit 13eae20

Please sign in to comment.