【问题标题】:How to fix this non-recursive odd-even-merge sort algorithm?如何修复这种非递归奇偶合并排序算法?
【发布时间】:2016-03-29 08:05:27
【问题描述】:

我正在搜索非递归奇偶合并排序算法并找到 2 个来源:

两种算法相同但错误。生成的排序网络不是奇偶合并排序网络。

这是具有 32 个输入的生成网络的图像。两条水平线之间的垂直线表示将值 a[x] 与 a[y] 进行比较,如果更大则交换数组中的值。


(来源:flylib.com
(可点击)

我将代码从 Java 复制到 C,并将 exch 函数替换为 printf 以打印交换候选人。

当绘制配对图时,可以看出生成了太多配对。

有人知道如何修复这个算法吗?

为什么我需要非递归版本?
我想把这个分类网络变成硬件。将流水线阶段插入到非递归算法中很容易。

我也研究了递归版本,但是将算法转换为流水线硬件太复杂了。

我的 C 代码:

#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\n", l+j+i, l+j+i+k);
}
int main(char* argv, int args)
{ const int COUNT = 8;
  sort(0, COUNT);
}

结果:

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 以在我的硬件平台上进行测试。

其他开源硬件排序网络实现:


附录:
奇偶合并排序(又名 Batcher 的排序)类似于双调排序(不要与 Batcher 的双调排序混淆)。但是在硬件上这个算法比双音排序有更好的尺寸复杂度,而延迟是一样的。

与快速排序等快速软件算法相比,这些算法可以以良好的资源利用率实现。

维基百科:odd-even mergesort

注意:
因为排序网络是静态的并且独立于输入值,所以生成网络不需要比较和交换。这就是它可以转变为硬件的原因之一。我的代码为比较操作生成索引。在硬件中,这些垂直连接将被比较和交换电路所取代。因此未排序的数据将通过网络传输并在输出端进行排序。

【问题讨论】:

  • 不知道在效率上你会努力到什么程度,但是如果最终结果是准确的,那么在这个过程中产生太多对真的很重要吗?
  • 是的。在软件中,它会产生大量的比较操作,并造成很大的缓存污染。在硬件中,它会增加面积消耗和延迟。通常奇偶合并排序具有 O(N * log N * log N) 大小的复杂度。我的图表看起来像 N^3。
  • 也许这有帮助? academia.edu/9035484/…。试试dsp.stackexchange.com
  • 我将结果图完成为 ASCII-art :)。
  • 谢谢,Paebbels。现在更清楚问题是什么了。第二列中的 2-3、4-5 和 6-7 排序显然是多余的。

标签: c sorting mergesort sorting-network


【解决方案1】:

我想我找到了解决办法。我查了length = 2, 4, 8, 16

这是我的代码:

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\n", x, y);
          }
        }
      }
   }
}

此解决方案引入了第五个 for 循环来处理组中的子块。 j 循环具有更改的 start 和 abort 值,以处理合并后步骤的奇数计数,而不会生成双倍比较步骤。

【讨论】:

    【解决方案2】:

    以下代码适用于任何大小的数组,并且不是递归的。它是 Perl 的 Algorithm::Networksort 模块中的 the corresponding function 实现的直接端口。该实现恰好对应于 Knuth 在计算机编程的艺术,第 3 卷中描述的算法(算法 5.2.2M)。它对实际修复您的算法没有帮助,但它至少为您提供了一个只有三个嵌套循环的 Batcher 奇偶合并排序的工作非递归实现:)

    #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\n", i, i + d);
                    }
                }
    
                d = q - p;
                q /= 2;
                r = p;
            }
            p /= 2;
        }
    }
    

    如果您能获得计算机编程的艺术,第 3 卷的副本,您将很好地解释该算法的工作原理和原理,以及一些额外的细节.

    【讨论】:

    • 我会试着在图书馆里找到这本书。
    【解决方案3】:

    这是一个固定的非递归子程序。

    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\n", i + j, i + j + k);
    }
    

    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\n", i + j, i + j + k);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-06
      • 1970-01-01
      • 1970-01-01
      • 2013-01-07
      • 1970-01-01
      • 2015-07-12
      • 1970-01-01
      相关资源
      最近更新 更多