379 lines
13 KiB
C++
379 lines
13 KiB
C++
#pragma once
|
|
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <future>
|
|
#include <ranges>
|
|
#include <span>
|
|
|
|
namespace algo {
|
|
|
|
class MergeSort_v1 {
|
|
private:
|
|
template<typename Iterator, typename Comparator>
|
|
static auto
|
|
merge(Iterator start, Iterator middle, Iterator end, Comparator cmp, Iterator output_start) -> void {
|
|
Iterator start_m = start;
|
|
Iterator begin = output_start;
|
|
Iterator start2 = middle + 1;
|
|
|
|
//merge from input until one half completes
|
|
while (start <= middle && start2 <= end) {
|
|
if (cmp(*start, *start2)) {
|
|
*output_start = *start;
|
|
start++;
|
|
} else {
|
|
*output_start = *start2;
|
|
start2++;
|
|
}
|
|
output_start++;
|
|
}
|
|
|
|
//try to finish first half
|
|
while (start <= middle) {
|
|
*output_start = *start;
|
|
start++;
|
|
output_start++;
|
|
}
|
|
|
|
while (start2 <= end) {
|
|
*output_start = *start2;
|
|
start2++;
|
|
output_start++;
|
|
}
|
|
|
|
const auto size = std::distance(start_m, end);
|
|
for (auto i = 0; i <= size; i++, start_m++, begin++) {
|
|
*start_m = *begin;
|
|
}
|
|
|
|
}
|
|
|
|
template<typename Container, typename Iterator, typename Comparator>
|
|
static auto
|
|
ms_split(Container &output_vec, Iterator start, Iterator end, Comparator cmp, Iterator output_start) -> void {
|
|
Iterator mid = start;
|
|
Iterator begin = output_start;
|
|
|
|
if (std::distance(start, end) < 1) {
|
|
return;
|
|
} else {
|
|
//move mid iterator litterally to the mid
|
|
std::advance(mid, std::distance(start, end) / 2);
|
|
//sort the first half within an recursion
|
|
ms_split(output_vec, start, mid, cmp, output_start);
|
|
|
|
//move output iterator
|
|
std::advance(output_start, std::distance(start, mid + 1));
|
|
//sort the second half within a recursion
|
|
ms_split(output_vec, mid + 1, end, cmp, output_start);
|
|
|
|
//merge everything together starting from the complete beginning
|
|
merge(start, mid, end, cmp, begin);
|
|
}
|
|
}
|
|
|
|
public:
|
|
template<typename Iterator, typename Comparator>
|
|
static auto sort(Iterator start, Iterator end, Comparator cmp) -> void {
|
|
using valtype = typename std::iterator_traits<Iterator>::value_type;
|
|
std::vector<valtype> temporary_dataset(std::distance(start, end));
|
|
ms_split(temporary_dataset, start, end - 1, cmp, temporary_dataset.begin());
|
|
}
|
|
};
|
|
|
|
class MergeSort_v2 {
|
|
private:
|
|
|
|
template<typename Container, typename Comparator>
|
|
static auto
|
|
mt_merge(Container left, Container right, Comparator cmp) -> Container {
|
|
//using Iterator = typename std::iterator_traits<Container>::value_type;
|
|
|
|
Container output;
|
|
|
|
auto lefti = left.begin();
|
|
auto righti = right.begin();
|
|
|
|
while (lefti < left.end() && righti < right.end()) {
|
|
if (cmp(*lefti, *righti)) {
|
|
output.emplace_back(std::move(*lefti));
|
|
lefti++;
|
|
} else {
|
|
output.emplace_back(std::move(*righti));
|
|
righti++;
|
|
}
|
|
}
|
|
|
|
while (lefti < left.end()) {
|
|
output.emplace_back(std::move(*lefti));
|
|
lefti++;
|
|
}
|
|
while (righti < right.end()) {
|
|
output.emplace_back(std::move(*righti));
|
|
righti++;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
template<typename Iterator, typename Comparator>
|
|
static auto
|
|
merge(Iterator start, Iterator middle, Iterator end, Comparator cmp, Iterator output_start,
|
|
std::recursive_mutex &dataset_guard) -> void {
|
|
Iterator start_m = start;
|
|
Iterator begin = output_start;
|
|
Iterator start2 = middle + 1;
|
|
|
|
//merge from input until one half completes
|
|
while (start <= middle && start2 <= end) {
|
|
if (cmp(*start, *start2)) {
|
|
*output_start = *start;
|
|
start++;
|
|
} else {
|
|
*output_start = *start2;
|
|
start2++;
|
|
}
|
|
output_start++;
|
|
}
|
|
|
|
//try to finish first half
|
|
while (start <= middle) {
|
|
*output_start = *start;
|
|
start++;
|
|
output_start++;
|
|
}
|
|
|
|
while (start2 <= end) {
|
|
*output_start = *start2;
|
|
start2++;
|
|
output_start++;
|
|
}
|
|
|
|
dataset_guard.lock();
|
|
const auto size = std::distance(start_m, end);
|
|
for (auto i = 0; i <= size; i++, start_m++, begin++) {
|
|
*start_m = *begin;
|
|
}
|
|
dataset_guard.unlock();
|
|
|
|
}
|
|
|
|
template<typename Container, typename Iterator, typename Comparator>
|
|
static auto mt_split(Container &output_vec, Iterator start, Iterator end, Comparator cmp, Iterator output_start,
|
|
int &nthreads, std::recursive_mutex &dataset_guard, std::mutex &depth_guard) -> void {
|
|
Iterator mid = start;
|
|
Iterator to_start = output_start;
|
|
|
|
if (std::distance(start, end) < 1) {
|
|
return;
|
|
|
|
}
|
|
|
|
bool rem_threads;
|
|
{
|
|
std::lock_guard guard(depth_guard); //RAII guard
|
|
rem_threads = nthreads > 1;
|
|
}
|
|
|
|
if (rem_threads) {
|
|
{
|
|
std::lock_guard guard(depth_guard); //RAII guard
|
|
nthreads -= 2;
|
|
}
|
|
|
|
std::advance(mid, std::distance(start, end) / 2);
|
|
std::thread t1([&]() {
|
|
mt_split(output_vec, start, mid, cmp, output_start, nthreads, dataset_guard, depth_guard);
|
|
});
|
|
|
|
std::advance(output_start, std::distance(start, mid + 1));
|
|
std::thread t2([&]() {
|
|
mt_split(output_vec, mid + 1, end, cmp, output_start, nthreads, dataset_guard, depth_guard);
|
|
});
|
|
|
|
|
|
//merge everything together starting from the complete beginning
|
|
t1.join();
|
|
t2.join();
|
|
std::vector<int> left;
|
|
left.assign(start, mid);
|
|
std::vector<int> right;
|
|
right.assign(mid + 1, end);
|
|
|
|
mt_merge(left, right, cmp);
|
|
} else {
|
|
//move mid iterator litterally to the mid
|
|
std::advance(mid, std::distance(start, end) / 2);
|
|
//sort the first half within an recursion
|
|
mt_split(output_vec, start, mid, cmp, output_start, nthreads, dataset_guard, depth_guard);
|
|
|
|
//move output iterator
|
|
std::advance(output_start, std::distance(start, mid + 1));
|
|
//sort the second half within a recursion
|
|
mt_split(output_vec, mid + 1, end, cmp, output_start, nthreads, dataset_guard, depth_guard);
|
|
|
|
std::vector<int> left;
|
|
left.assign(start, mid);
|
|
std::vector<int> right;
|
|
right.assign(mid + 1, end);
|
|
|
|
mt_merge(left, right, cmp);
|
|
}
|
|
}
|
|
|
|
template<typename Container, typename Iterator, typename Comparator>
|
|
static auto ms_split(Container &output_vec, Iterator start, Iterator end, Comparator cmp, Iterator output_start,
|
|
int &nthreads, std::recursive_mutex &dataset_guard, std::mutex &depth_guard) -> void {
|
|
Iterator mid = start;
|
|
Iterator begin = output_start;
|
|
|
|
if (std::distance(start, end) < 1) {
|
|
//Quit on smalles list size (one element is always sorted)
|
|
return;
|
|
} else {
|
|
if (nthreads > 1) {
|
|
depth_guard.lock();
|
|
nthreads -= 2;
|
|
depth_guard.unlock();
|
|
//move mid iterator litterally to the mid
|
|
std::advance(mid, std::distance(start, end) / 2);
|
|
//sort the first half within an recursion
|
|
|
|
std::thread t1([&]() {
|
|
ms_split(output_vec, start, mid, cmp, output_start, nthreads, dataset_guard, depth_guard);
|
|
});
|
|
|
|
//move output iteratoroutput_vec, start, mid, cmp, output_start
|
|
std::advance(output_start, std::distance(start, mid + 1));
|
|
//sort the second half within a recursion
|
|
std::thread t2([&]() {
|
|
ms_split(output_vec, mid + 1, end, cmp, output_start, nthreads, dataset_guard, depth_guard);
|
|
});
|
|
|
|
|
|
//merge everything together starting from the complete beginning
|
|
t1.join();
|
|
t2.join();
|
|
merge(start, mid, end, cmp, begin, dataset_guard);
|
|
} else {
|
|
//move mid iterator litterally to the mid
|
|
std::advance(mid, std::distance(start, end) / 2);
|
|
//sort the first half within an recursion
|
|
ms_split(output_vec, start, mid, cmp, output_start, nthreads, dataset_guard, depth_guard);
|
|
|
|
//move output iterator
|
|
std::advance(output_start, std::distance(start, mid + 1));
|
|
//sort the second half within a recursion
|
|
ms_split(output_vec, mid + 1, end, cmp, output_start, nthreads, dataset_guard, depth_guard);
|
|
|
|
//merge everything together starting from the complete beginning
|
|
merge(start, mid, end, cmp, begin, dataset_guard);
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
template<typename Iterator, typename Comparator>
|
|
static auto sort(Iterator start, Iterator end, Comparator cmp, int nthreads) -> void {
|
|
using valtype = typename std::iterator_traits<Iterator>::value_type;
|
|
std::vector<valtype> temporary_dataset(std::distance(start, end));
|
|
std::recursive_mutex dataset_guard;
|
|
std::mutex depth_guard;
|
|
|
|
mt_split(temporary_dataset, start, end - 1, cmp, temporary_dataset.begin(), nthreads, dataset_guard,
|
|
depth_guard);
|
|
}
|
|
};
|
|
|
|
class MergeSort_mt {
|
|
|
|
template<typename T, typename Comparator>
|
|
static auto
|
|
merge(std::vector<T> left, std::vector<T> right,
|
|
Comparator cmp, std::mutex &mut) -> std::vector<T> {
|
|
|
|
std::vector<T> output;
|
|
output.reserve(left.size() + right.size());
|
|
|
|
auto l = left.begin();
|
|
auto r = right.begin();
|
|
|
|
auto o = output.begin();
|
|
|
|
while (l < left.end() && r < right.end()) {
|
|
if (cmp(*l, *r)) {
|
|
output.insert(o, *l);
|
|
l++;
|
|
} else {
|
|
output.insert(o, *r);
|
|
r++;
|
|
}
|
|
o++;
|
|
}
|
|
while (l < left.end()) {
|
|
output.insert(o, *l);
|
|
o++;
|
|
l++;
|
|
}
|
|
while (r < right.end()) {
|
|
output.insert(o, *r);
|
|
o++;
|
|
r++;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
template<typename T, typename Comparator>
|
|
static auto split(std::vector<T> data, Comparator cmp, int depth, int &max_depth,
|
|
std::mutex &mut) -> std::vector<T>{
|
|
|
|
if (data.size() <= 1) {
|
|
return data;
|
|
} else if (data.size() == 2) {
|
|
if(cmp(data[0], data[1])) {
|
|
return std::vector<T> {data[0], data[1]};
|
|
} else {
|
|
return std::vector<T> {data[1], data[0]};
|
|
}
|
|
}
|
|
|
|
std::vector<T> output;
|
|
output.reserve(data.size());
|
|
|
|
auto mid = data.begin();
|
|
std::advance(mid, std::distance(data.begin(), data.end()) / 2);
|
|
|
|
|
|
std::vector<T> left(data.begin(), mid);
|
|
std::vector<T> right(mid, data.end());
|
|
|
|
if (depth < max_depth) {
|
|
std::thread left_thread([&]() { left = split(left, cmp, depth + 1, max_depth, mut); });
|
|
std::thread right_thread([&]() { right = split(right, cmp, depth + 1, max_depth, mut); });
|
|
|
|
left_thread.join();
|
|
right_thread.join();
|
|
} else {
|
|
left = split(left, cmp, depth + 1, max_depth, mut);
|
|
right = split(right, cmp, depth + 1, max_depth, mut);
|
|
}
|
|
|
|
return merge(left, right, cmp, mut);
|
|
}
|
|
|
|
public:
|
|
template<typename T, typename Comparator>
|
|
static auto
|
|
sort(std::vector<T> &data, Comparator cmp, int max_depth = 0) -> void {
|
|
std::mutex local_result_lock;
|
|
std::vector<T> output;
|
|
output.reserve(data.size());
|
|
|
|
output = split(data, cmp, 0, max_depth, local_result_lock);
|
|
data.assign(output.begin(), output.end());
|
|
}
|
|
};
|
|
} |