最低数量比较以找到 3 个数字的中位数
Posted
技术标签:
【中文标题】最低数量比较以找到 3 个数字的中位数【英文标题】:Minimum no. of comparisons to find median of 3 numbers 【发布时间】:2013-06-14 01:33:30 【问题描述】:我正在实施快速排序,我希望将枢轴设置为中位数或三个数字。这三个数字分别是第一个元素、中间元素和最后一个元素。
我能在 less 中找到中位数吗?比较?
median(int a[], int p, int r)
int m = (p+r)/2;
if(a[p] < a[m])
if(a[p] >= a[r])
return a[p];
else if(a[m] < a[r])
return a[m];
else
if(a[p] < a[r])
return a[p];
else if(a[m] >= a[r])
return a[m];
return a[r];
【问题讨论】:
你只关心比较次数吗?其他算术运算数是不是有界? 我只想要一个高效的代码来计算中位数。 那么你就有了。最好的情况是 2 次比较,最坏的情况是 3 次。 【参考方案1】:如果关注点只是比较,那么应该使用它。
int getMedian(int a, int b , int c)
int x = a-b;
int y = b-c;
int z = a-c;
if(x*y > 0) return b;
if(x*z > 0) return c;
return a;
【讨论】:
或者简单地使用三元运算符(C、C#、Java、javascript,...):((a-b)*(b-c) > -1 ? b : ((a-b)*(a-c) < 1 ? a : c))
【参考方案2】:
int32_t FindMedian(const int n1, const int n2, const int n3)
auto _min = min(n1, min(n2, n3));
auto _max = max(n1, max(n2, n3));
return (n1 + n2 + n3) - _min - _max;
【讨论】:
【参考方案3】:你不能一次完成,而且你只使用两个或三个,所以我会说你已经得到了最少的比较次数。
【讨论】:
可以对任意 3 个数字进行严格的 2 次比较吗? 不,例如,如果您选择了两个最小值,则必须有一个决胜局。尽管如果您愿意使用其他操作,则可以通过两次比较来摆脱困境。请参阅 Raghav 的回答。【参考方案4】:与其只计算中位数,不如将它们放在适当的位置。这样您就可以一直只进行 3 次比较,并且您的支点更接近到位。
T median(T a[], int low, int high)
int middle = ( low + high ) / 2;
if( a[ middle ].compareTo( a[ low ] ) < 0 )
swap( a, low, middle );
if( a[ high ].compareTo( a[ low ] ) < 0 )
swap( a, low, high );
if( a[ high ].compareTo( a[ middle ] ) < 0 )
swap( a, middle, high );
return a[middle];
【讨论】:
【参考方案5】:我知道这是一个旧线程,但我必须在一个 RAM 非常少且没有硬件乘法单元 (:) 的微控制器上解决这个问题。最后我发现以下效果很好:
static char medianIndex[] = 1, 1, 2, 0, 0, 2, 1, 1 ;
signed short getMedian(const signed short num[])
return num[medianIndex[(num[0] > num[1]) << 2 | (num[1] > num[2]) << 1 | (num[0] > num[2])]];
【讨论】:
【参考方案6】:如果您不怕弄脏编译器内在函数,您可以使用正好 0 个分支。
之前讨论过同样的问题:Fastest way of finding the middle value of a triple?
尽管如此,我必须补充一点,在快速排序的天真实现的上下文中,有很多元素,在找到中位数时减少分支的数量并不是那么重要,因为分支预测器会在你将开始在枢轴周围折腾元素。更复杂的实现(不在分区操作上分支,并避免 WAW 危害)将从中受益匪浅。
【讨论】:
【参考方案7】:从总和中删除最大值和最小值
int med3(int a, int b, int c)
int tot_v = a + b + c ;
int max_v = max(a, max(b, c));
int min_v = min(a, min(b, c));
return tot_v - max_v - min_v
【讨论】:
请尝试解释您的答案,添加一些 cmets。 这已经是这个问题的答案了:***.com/a/29242318/1816580【参考方案8】:实际上有一种巧妙的方法可以通过仔细分析 6 种可能的排列(低、中、高)来将中值元素从三个中分离出来。在python中:
def med(a, start, mid, last):
# put the median of a[start], a[mid], a[last] in the a[start] position
SM = a[start] < a[mid]
SL = a[start] < a[last]
if SM != SL:
return
ML = a[mid] < a[last]
m = mid if SM == ML else last
a[start], a[m] = a[m], a[start]
你有一半的时间有两个比较,否则你有 3 个(平均 2.5)。而且您只在需要时交换中值元素一次(2/3 的时间)。
完整的python快速排序在:
https://github.com/mckoss/labs/blob/master/qs.py
【讨论】:
其实不是 2.5,而是 2.666.. 因为你只在三分之一的情况下进行了两次比较(假设所有三个值都是根据相同的分布和加倍值的概率随机选择的i 0,即 a[start] 包含中位数的概率)。【参考方案9】:你可以写出所有的排列:
1 0 2
1 2 0
0 1 2
2 1 0
0 2 1
2 0 1
然后我们要找到1
的位置。如果我们的第一个比较可以拆分出一组相等的位置,例如前两行,我们可以通过两次比较来做到这一点。
问题似乎是前两行在我们可用的任何比较中都不同:a<b
、a<c
、b<c
。因此,我们必须完全识别排列,这需要在最坏的情况下进行 3 次比较。
【讨论】:
【参考方案10】:使用按位异或运算符,可以找到三个数字的中位数。
def median(a,b,c):
m = max(a,b,c)
n = min(a,b,c)
ans = m^n^a^b^c
return ans
【讨论】:
以上是关于最低数量比较以找到 3 个数字的中位数的主要内容,如果未能解决你的问题,请参考以下文章
算法初级面试题07——前缀树应用介绍和证明贪心策略拼接字符串得到最低字典序切金条问题项目收益最大化问题随时取中位数宣讲会安排
如何在 O(n) 时间内找到与 n 个不同数字的中位数最近的 k 个邻居?
不用排序怎样快速找到中位数,最好是一遍下来得到结果,求算法或者思路 谢谢!