这是其他答案的一些变体,与 6 次比较相比,它平均提高了 3.33% 到 66.67%,并且将 5 个元素围绕其中位数完全划分为奖励,无需额外费用。
通过使用 3 的中值和快速选择,使用 3 的中值从 5 个样本中的 3 个样本中选择枢轴,可以在平均 5.8 次比较(所有排列的平均值)中找到 5 个不同元素的中值元素。Median-of-3 对这三个元素进行分区,在对其余 2 个元素进行分区时,无需将其与枢轴进行重新比较。到目前为止,这是围绕三个中间元素之一划分 5 个元素的 4-5 次比较(3 的中位数不能是 5 的最小值或最大值)。最多需要 3 次比较才能将 5 个元素围绕它们的中位数进行划分(严格来说,这比仅仅找到中位数需要更多的工作),总共进行 4 到 7 次比较,(如前所述)平均为 5 次。5 个不同元素的所有可能排列中的 8 个(如果元素不不同,则比较较少)。请注意,这与通常的 always-6-comparisons 解决方案不同,因为少数不同输入的情况可能需要多达 7 次比较,但另一方面,不同输入的大多数排列需要不超过 6 次,并且通常更少; 此外,为非不同输入保存比较是相当容易的(如果所有输入都相等,则只需要 2 次比较;使用通常的 6 比较方法在输入不不同时保存比较的代码变得相当复杂(尝试一下!),没有它,即使所有输入都相等,它仍然需要进行 6 次比较)。因为少数不同输入的情况可能需要多达 7 次比较,但另一方面,大多数不同输入的排列需要不超过 6 次,而且通常更少;此外,为非不同输入保存比较是相当容易的(如果所有输入都相等,则只需要 2 次比较;使用通常的 6 比较方法在输入不不同时保存比较的代码变得相当复杂(尝试一下!),没有它,即使所有输入都相等,它仍然需要进行 6 次比较)。因为少数不同输入的情况可能需要多达 7 次比较,但另一方面,大多数不同输入的排列需要不超过 6 次,而且通常更少;此外,为非不同输入保存比较是相当容易的(如果所有输入都相等,则只需要 2 次比较;使用通常的 6 比较方法在输入不不同时保存比较的代码变得相当复杂(尝试一下!),没有它,即使所有输入都相等,它仍然需要进行 6 次比较)。
可以通过这种方式找到除中位数以外的订单统计信息:平均而言,可以找到第二个最小或第二个最大的数据(5.81666...比较),当然也可以通过 4 个比较找到最小值或最大值。
基于此,根据要求,这里有一个注释很重的函数,它使用可变参数比较函数返回指向 5 个元素的中位数的指针。它是用 C 语言编写的,但它应该在 quadrathorpe-y 异常或 sea ploose ploose 中同样有效。请注意,这仅返回指向中值元素的指针;它不会对元素进行分区(实际上它不会移动任何元素)。
/* a virtual swap, preserving both pointers for later use */
#define VSWAP(ma,mb,mt) mt=ma,ma=mb,mb=mt
/* a virtual swap only preserving the first pointer */
#define VSWAP2(ma,mb,munused) ma=mb
/* virtual rotation to the left; reverse the first 3 args for right rotation */
#define ROTATE(ma,mb,mc,mt) (mt)=(ma),(ma)=(mb),(mb)=(mc),(mc)=(mt)
/* median of 5 elements, no data movement, minimum (average 5.8) comparisons */
/* This implementation minimizes the number of comparisons made (min. 2) when
elements compare equal.
*/
/* As no elements are moved, the elements are of course not partitioned around
the element pointed to by the returned pointer (this differs from selection
of the median via quickselect).
*/
/* Results are biased toward the middle: pc, then pb or pd, last pa or pe. */
/* The implementation is based on a simulation of quickselect using partition3
to select a pivot from the middle three elements, with partitioning by
skipping over the 3 partitioned elements. For distinct inputs, it uses on
average 5.8 comparisons (averaged over all permutations of 5 distinct
elements); fewer for inputs with equal elements. That's an improvement of
about 3% over the usual 6-comparison implementation of median-of-5.
*/
void *fmed5(void *pa, void *pb, void *pc, void *pd, void *pe,
int(*compar)(const void *,const void *))
{
void *t;
int c, d;
/* First compare the 3 middle elements to determine their relative
ordering; pb will point to the minimum, pc to the median, and pd to
the maximum of the three.
*/
/* Ternary median-of-3, biased toward middle element if possible, else
first element. Average 8/3 comparisons for 3 elements (distinct values)
= 0.889 comparisons/element
*/
c=compar(pb,pc); /* 1 */
if (0<c) { /* *pb,*pc out-of-order: *pb>*pc */
/* Before doing anything about pb,pc, compare *pc,*pd. */
d=compar(pc,pd); /* 2a */
if (0>d) { /* *pc<*pd: strictly in order */
/* But *pb might be either larger than or smaller than (or equal
to) *pd, so they may (i.e. unless it's known from the earlier
comparison of original *pc and *pd that *pb is larger than
both) have to be compared, Possibilities:
*pc<*pb<=*pd (virtual swap of pb,pc corrects relationship)
*pc<*pd<*pb (virtual rotation to the left corrects it)
*/
c=compar(pb,pd); /* 3a (if needed) */
if (0<c) { /* *pc < *pd < *pb */
ROTATE(pb,pc,pd,t);
} else { /* *pc < *pb <= *pd */
VSWAP(pb,pc,t);
}
} else { /* *pd==*pc<*pb or reversed ordering: *pd<*pc<*pb */
VSWAP(pb,pd,t); /* one (virtual) swap takes care of it */
} /* *pc:*pd comparisons results if-else */
/* Note that if pc,pd compare equal, pc remains as the chosen
median (biased toward the middle element).
*/
} else if (0==c) { /* *pb,*pc compare equal */
/* Either pb or pc can be taken as the median; bias here is towards
pc, which is already in the middle position. But pd might be
the minimum of the three or the maximum (or it may also be equal
to both pb and pc).
*/
d=compar(pb,pd); /* 2b */
if (0<d) { /* pb,pd are out-of-order */
VSWAP(pb,pd,t);
}
} else { /* *pb,*pc in strict order: *pb < *pc; how about *pc,*pd? */
d=compar(pc,pd); /* 2c */
if (0<d) { /* *pc,*pd are strictly out-of-order: *pd < *pc */
/* But *pb might be either larger than or smaller than (or equal
to) *pd, so they may (i.e. unless it's known from the earlier
comparison of original *pc and *pd that *pb is larger than
both) have to be compared, Possibilities:
*pb<=*pd<*pc (virtual swap of pc,pd corrects relationship)
*pd<*pb<*pc (virtual rotation to the right corrects it)
*/
c=compar(pb,pd); /* 3b (if needed) */
if (0<c) { /* *pd < *pb < *pc */
ROTATE(pd,pc,pb,t);
} else { /* *pc < *pb <= *pd */
VSWAP(pc,pd,t);
}
} /* *pc:*pd comparisons results if-else */
/* Note that if pc,pd compare equal, pc remains as the chosen
median (biased toward the middle element).
*/
} /* *pb:*pc comparisons results if-else chain */
/* Now pb points to the minimum of pb,pc,pd; pc to the median, and pd
to the maximum.
*/
/* Special case: if all three middle elements compare equal (0==c==d),
any one can be returned as the median of 5, as it's impossible for
either of the other two elements to be the median (unless of course
one or both of them also compares equal to pb,pc,pd, in which case
returning any of pb,pc,pd is still correct). Nothing more needs to
be done in that case.
*/
if ((0!=c)||(0!=d)) { /* Not all three of pb,pc,pd compare equal */
int e;
/* Compare pa and pe to pc. */
e=compar(pa,pc); /* 3c or 4a (iff needed) */
/* If three (or more) of the four elements so far compared are
equal, any of those equal-comparing elements can be chhosen as
the median of 5. If all five elements were arranged in order,
one of the three equal-comparing elements would necessarily be
in the middle (at most both of the remaining elements might be
either larger or smaller than the equal elements). So if pa
compares equal to pc and pc also compared equal to pb or to pd,
nothing more need be done; pc can be considered as the median of
five.
*/
if ((0!=e)||(0!=c)||(0!=d)) { /* no three elements compare equal */
int f;
f=compar(pe,pc); /* 4b or 5a (iff needed) */
/* Check again for three equal comparisons to avoid doing any
unnecessary additional work.
*/
if (
(0!=f) /* also not equal; still not three */
||
( /* 0==f && */
((0!=c)&&(0!=d)) /* at most e and f; not three */
||
((0!=c)&&(0!=e)) /* at most d and f; not three */
||
((0!=d)&&(0!=e)) /* at most c and f; not three */
)
) {
/* Possibilites are that:
one of *pa,*pe is less than (or equal to) *pc and one
is greater than (or equal to) *pc; *pc is the median
of five.
*pa and *pe are both less than *pc; the median of five
is then the maximum of *pa,*pb,*pe (*pc and *pd are
at least as large as those three). The ordering of
those 3 elements has not been established, and it
will take two additional comparisons to do so.
*pa and *pe are both greater than *pc; the median of
five is the minimum of *pa,*pd,*pe (neither *pb nor
*pc can be larger than any of those three).
*/
if ((0<e)&&(0<f)) { /* *pa,*pe both > *pc; median of five is
the minimum of *pa,*pe,*pd
*/
/* Bias towards *pd (originally closest of these three
to the middle. Neither *pa nor *pe has yet been
compared to *pd.
*/
e=compar(pa,pe); /* 5b or 6a (iff needed) */
if (0<e) { /* *pe is smaller */
f=compar(pd,pe); /* 6b or 7a (iff needed) */
if (0<f) { /* *pe is smaller */
VSWAP2(pc,pe,t);
} else { /* *pd is smaller or *pd==*pe */
VSWAP2(pc,pd,t);
}
} else { /* *pa==*pe or *pa is smaller */
f=compar(pd,pa); /* 6c or 7b (iff needed) */
if (0<f) { /* *pa is smaller */
VSWAP2(pc,pa,t);
} else { /* *pd is smaller or *pd==*pa */
VSWAP2(pc,pd,t);
}
}
} else if ((0>e)&&(0>f)) { /* *pa,*pe both < *pc; median of
five is the maximum of *pa,*pb,*pe
*/
/* Bias towards *pb (originally closest of these three
to the middle. Neither *pa nor *pe has yet been
compared to *pb.
*/
e=compar(pa,pe); /* 5c or 6d (iff needed) */
if (0<e) { /* *pa is larger */
f=compar(pa,pb); /* 6e or 7c (iff needed) */
if (0<f) { /* *pa is larger */
VSWAP2(pc,pa,t);
} else { /* *pb is larger or *pa==*pb */
VSWAP2(pc,pb,t);
}
} else { /* *pe is larger or *pa==*pe */
f=compar(pe,pb); /* 6f or 7d (iff needed) */
if (0<f) { /* *pe is larger */
VSWAP2(pc,pe,t);
} else { /* *pb is larger or *pe==*pb */
VSWAP2(pc,pb,t);
}
} /* *pa:*pe results if-else */
} /* median of five: minimum or maximum of three if-else */
} /* not three equal elements among five */
} /* not three equal elements among four */
} /* not three equal elements among three */
return pc;
}