提问者对基于排序网络的 5 中位数实现特别感兴趣,因此我将讨论这个具体案例。对文献的简要回顾表明,根据各种指标,最佳解决方案是什么样的。
Michael Codish、Luís Cruz-Filipe、Thorsten Ehlers、Mike Müller 和 Peter Schneider-Kamp。 “排序网络:到最后再回来。” Journal of Computer and System Sciences (2016) 在表 1 中显示,对于 n=5,比较交换的最小数量为 9,并且网络的最小深度是 5。由于每个比较交换相当于两个最小/最大操作,
所需的最小/最大操作数为 18。
Lukáŝ Sekanina,“中值电路的进化设计空间探索”。在:EvoWorkshops,2004 年 3 月,第 240-249 页,给出了最佳五输入中值选择网络所需的最小/最大操作数,如表 1 中的 10。
William Gasarch、Wayne Kelly 和 William Pugh。 “为小 i, n 找到第 i 个最大的 n。” ACM SIGACT 新闻 27,第 2 期。 2(1996):88-96,表 1:
对于 5 的中位数,至少需要进行 6 次比较。
通常,具有最少操作数的排序网络不会通过消除冗余操作而简化为具有最少操作数的中值选择网络。但是有可能找到至少对于 n 的某些值以最佳方式减少的网络。对于 n=5,对此类网络进行暴力搜索是可行的。
下面的代码显示了五输入排序网络的四种变体,包括九个比较交换操作,或者,18 个最小/最大操作。当使用FULL_SORT = 0 编译时,它们会变成具有 10 次最小/最大操作的中值选择网络。所以根据这个指标,排序和中值选择都是最优的。
但是,当用作排序网络时,所有四个变体的深度均为 6,而不是最小值为 5。此外,当配置为基于比较而不是 min/max 操作的中值选择网络时,都需要七次而不是最少的六次比较。
Compiler Explorer (godbolt) 的编译结果示例:对五输入 sort 使用 18 分钟/最大操作,对五输入 median 使用 10 分钟/最大操作。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define VARIANT 1
#define USE_MIN_MAX 1
#define FULL_SORT 0
typedef float T;
#if USE_MIN_MAX
#define MIN(a,b) ((T)(fmin(a,b)))
#define MAX(a,b) ((T)(fmax(a,b)))
#define SWAP(i,j) do { T s = MIN(a##i,a##j); T t = MAX(a##i,a##j); a##i = s; a##j = t; } while (0)
#else // USE_MIN_MAX
#define MIN(a,b) (((a) > (b)) ? (b) : (a))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define SWAP(i,j) do { if (a##i > a##j) { T temp = a##i; a##i = a##j; a##j = temp; }} while (0)
#endif // USE_MIN_MAX
/* Use sorting/median network to fully or partially sort array of five values
and return the median value
*/
T network5 (T *a)
{
// copy to scalars
T a0, a1, a2, a3, a4;
a0=a[0];a1=a[1];a2=a[2];a3=a[3];a4=a[4];
#if VARIANT == 1
SWAP (0, 1); SWAP (2, 3);
SWAP (0, 2); SWAP (1, 3);
SWAP (2, 1);
SWAP (1, 4);
SWAP (1, 2);
SWAP (0, 1); SWAP (3, 4);
#elif VARIANT == 2
SWAP (3, 4); SWAP (0, 2);
SWAP (2, 4); SWAP (0, 3);
SWAP (2, 3);
SWAP (1, 2);
SWAP (0, 1); SWAP (2, 3);
SWAP (3, 4);
#elif VARIANT == 3
SWAP (3, 2); SWAP (0, 4);
SWAP (2, 4); SWAP (0, 3);
SWAP (2, 3);
SWAP (1, 2);
SWAP (0, 1); SWAP (2, 3);
SWAP (3, 4);
#elif VARIANT == 4
SWAP (2, 4); SWAP (0, 1);
SWAP (0, 2); SWAP (1, 4);
SWAP (2, 3);
SWAP (1, 2);
SWAP (2, 3); SWAP (0, 1);
SWAP (3, 4);
#else
#error unsupported VARIANT
#endif
#if FULL_SORT
// copy back sorted results
a[0]=a0;a[1]=a1;a[2]=a2;a[3]=a3;a[4]=a4;
#endif
// return median-of-5
return a2;
}