How to fix this non-recursive odd-even-merge sort algorithm?
我正在搜索非递归奇偶合并排序算法,并找到2个来源:
- Sedgewick R.的书
- 这个问题
两种算法都相同,但都是错误的。生成的排序网络不是奇偶合并排序网络。
这是具有32个输入的结果网络的图像。 2条水平线之间的垂直线表示比较值a [x]和a [y],如果更大则交换数组中的值。
(来源:flylib.com) sub>
(可点击)
我将代码从Java复制到C并用
当绘制一对图时,可以看出生成了太多的对。
有人知道如何解决此算法吗?
为什么我需要非递归版本?
我想将此分类网络转换为硬件。将流水线阶段插入非递归算法很容易。
我还研究了递归版本,但是它太复杂了,无法将算法转换为流水线硬件。
我的C代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <stdlib.h> #include <stdio.h> void sort(int l, int r) { int n = r-l+1; for (int p=1; p<n; p+=p) for (int k=p; k>0; k/=2) for (int j=k%p; j+k<n; j+=(k+k)) for (int i=0; i<n-j-k; i++) if ((j+i)/(p+p) == (j+i+k)/(p+p)) printf("%2i cmp %2i\ ", l+j+i, l+j+i+k); } int main(char* argv, int args) { const int COUNT = 8; sort(0, COUNT); } |
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 0 -o--------o-------------------------o---------------o------------------------- | | | | 1 -o--------|-o------o----------------|-o-------------o-o----------------------- | | | | | | 2 -o-o------o-|------o-o--------------|-|-o----o--------o-o--------------------- | | | | | | | | | 3 -o-o--------o--------o--------------|-|-|-o--|-o--------o-o-------o----------- | | | | | | | | 4 -o-o-o----o---o----o-----o----------o-|-|-|--o-|-o--------o-o-----o-o--------- | | | | | | | | | | | | | | 5 -o-o-o----|-o-|-o--o-o---o-o---o------o-|-|----o-|-o--------o-o-----o-o---o--- | | | | | | | | | | | | | | 6 -o-o-o-o--o-|-o-|----o-o---o-o-o-o------o-|------o-|----------o-o-----o-o-o-o- | | | | | | | | | | | | | | 7 -o-o-o-o----o---o------o-----o---o--------o--------o------------o-------o---o- |
当我知道正确的交换对并且算法等于镜像时,我会将其转换为VHDL以在我的硬件平台上进行测试。
其他开源硬件分类网络实现:
- PoC.sort.sortnet.oddevensort
- PoC.sort.sortnet.bitonicsort
附录:
奇偶归并排序(也就是Batcher的排序)就像bionic排序(不要与Batcher的bitonic排序混淆)。但是在硬件上,此算法的大小复杂度比双音阶排序好,而延迟时间相同。
与快速排序算法(例如quicksort)相比,这些算法可以实现良好的资源利用率。
维基百科:单双兼并
注意:
由于排序网络是静态的,并且与输入值无关,因此不需要比较和交换即可生成网络。这就是为什么可以将其转换为硬件的原因之一。我的代码为比较操作生成索引。在硬件中,这些垂直连接将由比较和交换电路代替。因此,未分类的数据将通过网络传播,并在输出端将被分类。
以下代码适用于任何大小的数组,并且不递归。这是Perl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #include <math.h> #include <stdio.h> void oddeven_merge_sort(int length) { int t = ceil(log2(length)); int p = pow(2, t - 1); while (p > 0) { int q = pow(2, t - 1); int r = 0; int d = p; while (d > 0) { for (int i = 0 ; i < length - d ; ++i) { if ((i & p) == r) { printf("%2i cmp %2i\ ", i, i + d); } } d = q - p; q /= 2; r = p; } p /= 2; } } |
如果您可以获取《计算机编程艺术》第3卷的副本,那么您将对算法的工作原理和原因以及其他一些细节有很好的解释。
这是固定的非递归子例程。
1 2 3 4 5 6 7 8 9 10 11 | void sort(int n) { for (int p = 1; p < n; p += p) for (int k = p; k > 0; k /= 2) for (int j = k % p; j + k < n; j += k + k) //for (int i = 0; i < n - (j + k); i++) // wrong for (int i = 0; i < k; i++) // correct if ((i + j)/(p + p) == (i + j + k)/(p + p)) printf("%2i cmp %2i\ ", i + j, i + j + k); } |
或
1 2 3 4 5 6 7 8 9 10 | void sort(int n) { for (int p = 1; p < n; p += p) for (int k = p; k > 0; k /= 2) for (int j = 0; j < k; j++) for (int i = k % p; i + k < n; i += k + k) if ((i + j)/(p + p) == (i + j + k)/(p + p)) printf("%2i cmp %2i\ ", i + j, i + j + k); } |
我想我找到了解决方案。我检查了
这是我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void sort(int length) { int G = log2ceil(length); // number of groups for (int g = 0; g < G; g++) // iterate groups { int B = 1 << (G - g - 1); // number of blocks for (int b = 0; b < B; b++) // iterate blocks in a group { for (int s = 0; s <= g; s++) // iterate stages in a block { int d = 1 << (g - s); // compare distance int J = (s == 0) ? 0 : d; // starting point for (int j = J; j+d < (2<<g); j += 2*d) // iterate startpoints { for (int i = 0; i < d; i++) // shift startpoints { int x = (b * (length / B)) + j + i; // index 1 int y = x + d; // index 2 printf("%2i cmp %2i\ ", x, y); } } } } } |
此解决方案引入了第五个for循环来处理组中的子块。
j循环具有更改的开始和中止值,以处理合并后步骤的奇数计数,而不会生成加倍的比较步骤。