【问题标题】:Best Sort function for short arrays短数组的最佳排序函数
【发布时间】:2012-04-01 00:56:08
【问题描述】:

我正在研究一种处理图片的算法。 基本上我将实现一个扩散(每个像素将获得周围 8 个像素的中值 + 它自己的值)。

我要做的是用值创建一个由 9 个整数组成的数组,对数组进行排序并在数组 [4] 处获取中值。

我仍然不知道该用什么来解决这个问题,对于相对较小的数组,最好的排序函数是什么?排序函数将大致调用 x 次,x 是像素数。

堆排序似乎有点矫枉过正。快速排序不会表现得那么好。而且我不想实现非常复杂的东西。

你们怎么看?

【问题讨论】:

标签: c arrays sorting


【解决方案1】:

如果您只需要中位数,则根本不需要进行任何排序! (对于长数组,请参阅 http://en.wikipedia.org/wiki/Selection_algorithm 了解 O(n) 算法;当然我们这里只讨论短数组)。

对于 9 个数字的中值,稍微搜索一下 N.Devillard 的文章 Fast median search: an ANSI C implementation,它指向 JL Smith 的文章在 XC4000E FPGA 中实现中值滤波器,该文章提供了这种自解释性“排序网络”使用 19 次比较获得中位数:

在 C 方面:

typedef int T;

void sort2(T* a, T* b);
void sort3(T* a, T* b, T* c);
T min3(T a, T b, T c);
T max3(T a, T b, T c);

T median9(T p1, T p2, T p3, T p4, T p5, T p6, T p7, T p8, T p9)
{
    sort3(&p1, &p2, &p3);
    sort3(&p4, &p5, &p6);
    sort3(&p7, &p8, &p9);

    p7 = max3(p1, p4, p7);
    p3 = min3(p3, p6, p9);

    sort3(&p2, &p5, &p8);
    sort3(&p3, &p5, &p7);

    return p5;
}

void sort2(T* a, T* b)
{
    if (*a > *b)
    {
        T tmp = *b;
        *b = *a;
        *a = tmp;
    }
}

void sort3(T* a, T* b, T* c)
{
    sort2(b, c);
    sort2(a, b);
    sort2(b, c);
}

T min3(T a, T b, T c)
{
    if (a < b)
        return a < c ? a : c;
    else
        return b < c ? b : c;
}

T max3(T a, T b, T c)
{
    if (a > b)
        return a > c ? a : c;
    else
        return b > c ? b : c;
}

编辑:this file 还包含获取 3、5、6、7、9 和 25 数字中位数的代码。

#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
#define PIX_SWAP(a,b) { pixelvalue temp=(a);(a)=(b);(b)=temp; }

/*----------------------------------------------------------------------------
   Function :   opt_med9()
   In       :   pointer to an array of 9 pixelvalues
   Out      :   a pixelvalue
   Job      :   optimized search of the median of 9 pixelvalues
   Notice   :   in theory, cannot go faster without assumptions on the
                signal.
                Formula from:
                XILINX XCELL magazine, vol. 23 by John L. Smith

                The input array is modified in the process
                The result array is guaranteed to contain the median
                value
                in middle position, but other elements are NOT sorted.
 ---------------------------------------------------------------------------*/

pixelvalue opt_med9(pixelvalue * p)
{
    PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
    PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ;
    PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
    PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ;
    PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ;
    PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ;
    PIX_SORT(p[4], p[2]) ; return(p[4]) ;
}

【讨论】:

  • +1。终于有人指出,你不需要一个完整的排序。加上一些非常干净的代码。
  • 使用排序网络(如 Jeff Foster 的链接所建议的那样),您最终会得到 25 次比较,因此开销约为 30%...
  • 确实排序网络意味着 n 大,与总运行时间相比,开销可以认为是小的。然而,这被忽略了。
  • 性能点:sort3 的最后两次调用(在median9 内)可能被替换为找到中值的东西,而不是对所有 3 个元素进行排序
  • @valdo, nice: T med3(a,b,c){ return a&lt;b ? (c&lt;a ? a : (c&lt;b?c:b)) : (c&lt;b ? b : (c&lt;a?c:a))} 将最佳情况减少到 17 次比较。最坏的情况仍然是 19 岁。
【解决方案2】:

你知道 c 库的“标准”qsort 通常是相当优化的吗?通常它是小型子数组的快速排序+插入排序(当您细分基本数组时)。比如read the comments这个版本取自gnu c库

【讨论】:

  • qsort 可能会因为这两个原因而感到震惊。 (1) 9 是一个很小的数字。 (2) 每次比较操作都会产生函数调用开销(这就是为什么 C++ 的std::sortqsort 快得多)。
  • @OliCharlesworth 当我遇到这些问题时,我通常会从 BSD 许可版本中获取源代码并替换对我无用的代码部分 :-)(所以我会替换所有对直接比较的比较器)
  • 必须使用 qsort 而不是归并排序的唯一原因是空间复杂度,归并排序的 O(logn) vs O(n)。如果您要使用库实现,请使用 O(nlogn) 时间复杂度的归并排序,这使其成为小型数组的不错选择。
  • @g24l 很简单:除非我有非常特殊的问题,否则我需要使用qsort(c 库之一)的单行代码比使用排序算法维护另一个源文件要好得多。显然,如果我的业务是“排序”,那么我将花费我的时间寻找如何以最好的方式进行排序......(我可能不会走到这个 stackoverflow.com/questions/2786899/… 但至少我会比较好的实现“标准”排序算法)。我的观点是,OP 会对默认的 qsort 非常满意。
  • 您是否注意到他说“快速排序不会表现得那么好”。其余的我同意你的看法。
【解决方案3】:

从理论上讲,您想要找到一个中位数,这是selection problem 的一个特例。它可以在线性时间内完成。

但这是理论上的。实际上,我会使用“基于分区的通用选择算法”(在文章中提到)。它平均在线性时间内工作,而且它实际上简单快速。

template <typename T>
T* SubDivide(T* pB, T* pE)
{
    ASSERT(pB < pE);

    T* pPivot = --pE;
    const T pivot = *pPivot;

    while (pB < pE)
    {
        if (*pB > pivot)
        {
            --pE;
            std::swap(*pB, *pE);
        } else
            ++pB;
    }

    std::swap(*pE, *pPivot);
    return pE;
}

template <typename T>
void SelectElement(T* pB, T* pE, size_t k)
{
    ASSERT((pE > pB) && (k < unsigned(pE - pB)));

    while (true)
    {
        T* pPivot = SubDivide(pB, pE);
        size_t n = pPivot - pB;

        if (n == k)
            break;

        if (n > k)
            pE = pPivot;
        else
        {
            pB = pPivot + 1;
            k -= (n + 1);
        }
    }
}

// Usage example
int pArr[9] = { /* fill with values*/ };

// Select the 4th element (which is a median)
SelectElement(pArr, pArr + 9, 4);

// pArr[4] now holds the median value

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-27
    • 2012-05-18
    • 2010-09-07
    • 1970-01-01
    • 1970-01-01
    • 2017-05-23
    • 2022-11-17
    • 2011-11-27
    相关资源
    最近更新 更多