如果我将其并行化,我试图直观地了解我可以加快合并排序的速度。
到目前为止我的想法:
如果N是要排序的数组中的元素数,则log(base 2)N是我需要的最大核心数。我相信这是因为mergesort中有2 * log(base 2)N + 1级别。首先,你将它一遍又一遍地将其分解,然后你反复合并两个已排序的数组,直到你再次有一个N个项目的数组(现在它已被排序)。
我正在试图弄清楚这实际上会提高多少性能。我认为随着我们向算法的中间移动,由于额外的核心而导致的性能提升会增加,因为我们可以使用更多核心。假设我在未排序的数组中有16个项目。我只需要使用一个核心将其分解为两个8项目数组,然后我可以使用两个核心将它们分成四个4项目数组等。
因此,对于每个分割级别,性能将增加两倍,然后对于每个合并级别减少两个......对吗?我在这里走在正确的轨道上吗?
另外,为什么我们不能首先合并未排序数组中的前两项,然后合并接下来的两项,依此类推。基本上摆脱了算法的前半部分?
思想?
我应该在math.stackexchange.com上问这个吗?对不起,如果是这样......我真的不知道
答案 0 :(得分:0)
如果要通过并行化提高MergeSort的性能,则应该并行化拆分(合并结果之前执行的部分)。我假设你有多个CPU节点。
<强>分裂:强> 让当前的CPU节点保留一半的数组,并将另一半的CPU节点提供给另一个CPU节点。继续重复这个过程。当你深入到树中时,并行性会增加(如你所提到的)
基本情况: 当数据是一个项目时,当前CPU节点将其发送回其父节点。 在进行任何合并之前,父节点将等待子节点传递数据。
<强>合并:强> 一旦接收到来自节点子节点的数据,节点(该子节点的父节点)就可以开始将接收到的数据与其自己的数据合并。合并完成后,它会将其传递给其父节点,依此类推。由于每个节点都是单独的CPU,因此并行执行较低级别的合并。当我们上树时,这种并行性会降低。 (就像你提到的那样)
这应该加快合并排序。
但是,维基百科http://en.wikipedia.org/wiki/Merge_sort#Parallel_processing上的这篇文章表明,通过并行化和专门化(以及在数据大小<11时抛出插入排序),可以加快合并步骤到O(1)
我很好奇为什么你不使用Quicksort。它很适合并行化!
编辑:
另外,为什么我们不能开始合并前两个项目 未排序的数组,然后是下两个,依此类推。基本上摆脱了 算法的前半部分?
并回答您的问题:
这就是合并排序正在做的事情,它正在合并前2个,接下来的2个,依此类推,但为了得到它们,它使用了递归。这使得运行时O(n * 2log(n))因为有2个树(一个在分割时创建,一个在合并回一个大列表时创建)。这出现在O(nlog(n))。
采取你的想法,从底部开始,取2乘2的数字并对它们进行排序。然后扩大边界以包含2个块(每个w 2个数字)4个数字......等等。你正在建造一棵树,从树叶到树根。这类似于锦标赛算法(尽管你只有一个胜利者 - 树的根)。
运行时:首先你有n个数字。你循环所以设置每2个数字O(n / 2),下一个O(n / 4),下一个O(n / 8)等边界。 构建此树需要O(log(n))。但是你仍然需要将其他数字合并到一个列表中。由于您有n个数字,即n * O(nlogn),它为您提供与merge sort nlogn相同的运行时。
<强>要点:强> 所以我想说的是你从底层合并的想法仍然是长久的。你正在摆脱其中一棵树,所以速度并不快。
答案 1 :(得分:0)
众所周知,Dual Pivot QuickSort在串行版本中击败了Merge Sort。我认为DPQ的并行形式可能真的是有史以来最快的排序算法。原因是它具有比MergeSort低的常数因子,并且其最坏情况时间复杂度仅出现1 /(n!)的概率。如果N很大,则更喜欢DPQ,如果可能的话,可能是多线程的。但并行性有一个支点或限制,低于限制因其线程管理而变慢。超出限制它会更快。如果您感兴趣,下面的序列号(升序和降序)</ p>
protected static void ASC(int[]a, int left, int right, int div)
{
int len = 1 + right - left;
if (len < 27)
{
// insertion sort for small array
int P1 = left + 1;
int P2 = left;
while ( P1 <= right )
{
div = a[P1];
while(( P2 >= left )&&( a[P2] > div ))
{
a[P2 + 1] = a[P2];
P2--;
}
a[P2 + 1] = div;
P2 = P1;
P1++;
}
return;
}
int third = len / div;
// "medians"
int P1 = left + third;
int P2 = right - third;
if (P1 <= left)
{
P1 = left + 1;
}
if (P2 >= right)
{
P2 = right - 1;
}
int temp;
if (a[P1] < a[P2])
{
temp = a[P1]; a[P1] = a[left]; a[left] = temp;
temp = a[P2]; a[P2] = a[right]; a[right] = temp;
}
else
{
temp = a[P1]; a[P1] = a[right]; a[right] = temp;
temp = a[P2]; a[P2] = a[left]; a[left] = temp;
}
// pivots
int pivot1 = a[left];
int pivot2 = a[right];
// pointers
int less = left + 1;
int great = right - 1;
// sorting
for (int k = less; k <= great; k++)
{
if (a[k] < pivot1)
{
temp = a[k]; a[k] = a[less]; a[less] = temp;
less++;
}
else if (a[k] > pivot2)
{
while (k < great && a[great] > pivot2)
{
great--;
}
temp = a[k]; a[k] = a[great]; a[great] = temp;
great--;
if (a[k] < pivot1)
{
temp = a[k]; a[k] = a[less]; a[less] = temp;
less++;
}
}
}
int dist = great - less;
if (dist < 13)
{
div++;
}
temp = a[less-1]; a[less-1] = a[left]; a[left] = temp;
temp = a[great+1]; a[great+1] = a[right]; a[right] = temp;
// subarrays
ASC(a, left, less - 2, div);
ASC(a, great + 2, right, div);
// equal elements
if (dist > len - 13 && pivot1 != pivot2)
{
for (int k = less; k <= great; k++)
{
if (a[k] == pivot1)
{
temp = a[k]; a[k] = a[less]; a[less] = temp;
less++;
}
else if (a[k] == pivot2)
{
temp = a[k]; a[k] = a[great]; a[great] = temp;
great--;
if (a[k] == pivot1)
{
temp = a[k]; a[k] = a[less]; a[less] = temp;
less++;
}
}
}
}
// subarray
if (pivot1 < pivot2)
{
ASC(a, less, great, div);
}
}
protected static void DSC(int[]a, int left, int right, int div)
{
int len = 1 + right - left;
if (len < 27)
{
// insertion sort for large array
int P1 = left + 1;
int P2 = left;
while ( P1 <= right )
{
div = a[P1];
while(( P2 >= left )&&( a[P2] < div ))
{
a[P2 + 1] = a[P2];
P2--;
}
a[P2 + 1] = div;
P2 = P1;
P1++;
}
return;
}
int third = len / div;
// "medians"
int P1 = left + third;
int P2 = right - third;
if (P1 >= left)
{
P1 = left + 1;
}
if (P2 <= right)
{
P2 = right - 1;
}
int temp;
if (a[P1] > a[P2])
{
temp = a[P1]; a[P1] = a[left]; a[left] = temp;
temp = a[P2]; a[P2] = a[right]; a[right] = temp;
}
else
{
temp = a[P1]; a[P1] = a[right]; a[right] = temp;
temp = a[P2]; a[P2] = a[left]; a[left] = temp;
}
// pivots
int pivot1 = a[left];
int pivot2 = a[right];
// pointers
int less = left + 1;
int great = right - 1;
// sorting
for (int k = less; k <= great; k++)
{
if (a[k] > pivot1)
{
temp = a[k]; a[k] = a[less]; a[less] = temp;
less++;
}
else if (a[k] < pivot2)
{
while (k < great && a[great] < pivot2)
{
great--;
}
temp = a[k]; a[k] = a[great]; a[great] = temp;
great--;
if (a[k] > pivot1)
{
temp = a[k]; a[k] = a[less]; a[less] = temp;
less++;
}
}
}
int dist = great - less;
if (dist < 13)
{
div++;
}
temp = a[less-1]; a[less-1] = a[left]; a[left] = temp;
temp = a[great+1]; a[great+1] = a[right]; a[right] = temp;
// subarrays
DSC(a, left, less - 2, div);
DSC(a, great + 2, right, div);
// equal elements
if (dist > len - 13 && pivot1 != pivot2)
{
for (int k = less; k <= great; k++)
{
if (a[k] == pivot1)
{
temp = a[k]; a[k] = a[less]; a[less] = temp;
less++;
}
else if (a[k] == pivot2)
{
temp = a[k]; a[k] = a[great]; a[great] = temp;
great--;
if (a[k] == pivot1)
{
temp = a[k]; a[k] = a[less]; a[less] = temp;
less++;
}
}
}
}
// subarray
if (pivot1 > pivot2)
{
DSC(a, less, great, div);
}
}