49

给定的是一个由三个数值组成的数组,我想知道这三个数值的中间值。

问题是,找到三者中间的最快方法是什么?

我的方法是这种模式 - 因为有三个数字,所以有六个排列:

if (array[randomIndexA] >= array[randomIndexB] &&
    array[randomIndexB] >= array[randomIndexC])

如果有人能帮助我找到一种更优雅更快捷的方法,那就太好了。

4

25 回答 25

94

这里有一个使用 min/max 且没有分支的答案(https://stackoverflow.com/a/14676309/2233603)。实际上 4 min/max 操作足以找到中位数,不需要 xor:

median = max(min(a,b), min(max(a,b),c));

虽然,它不会给你中值的指数......

所有案例的细分:

a b c
1 2 3   max(min(1,2), min(max(1,2),3)) = max(1, min(2,3)) = max(1, 2) = 2
1 3 2   max(min(1,3), min(max(1,3),2)) = max(1, min(3,2)) = max(1, 2) = 2
2 1 3   max(min(2,1), min(max(2,1),3)) = max(1, min(2,3)) = max(1, 2) = 2
2 3 1   max(min(2,3), min(max(2,3),1)) = max(2, min(3,1)) = max(2, 1) = 2
3 1 2   max(min(3,1), min(max(3,1),2)) = max(1, min(3,2)) = max(1, 2) = 2
3 2 1   max(min(3,2), min(max(3,2),1)) = max(2, min(3,1)) = max(2, 1) = 2
于 2013-09-27T07:58:13.930 回答
35

如果硬件可以在没有分支的情况下回答最小和最大查询(今天的大多数 CPU 都可以做到这一点),那么就可以在没有分支的情况下回答查询。

运算符 ^ 表示按位异或。

Input: triple (a,b,c)
1. mx=max(max(a,b),c)
2. mn=min(min(a,b),c)
3. md=a^b^c^mx^mn
4. return md

这是正确的,因为:

  • xor 是可交换的和关联的
  • 相等位上的异或产生零
  • xor 为零不会改变位

应为 int/float 选择适当的 min/max 函数。如果仅存在正浮点数,则可以直接在浮点表示上使用整数 min/max(这可能是可取的,因为整数运算通常更快)。

在硬件不支持最小/最大的不太可能的情况下,可以执行以下操作:

max(a,b)=(a+b+|a-b|)/2
min(a,b)=(a+b-|a-b|)/2

但是,在使用浮点运算时这是不正确的,因为需要精确的最小值/最大值,而不是接近它的东西。幸运的是,硬件多年来一直支持浮点最小值/最大值(在 x86 上,从 Pentium III 及更高版本)。

于 2013-02-03T19:24:35.387 回答
29

如果您正在寻找最有效的解决方案,我想它是这样的:

if (array[randomIndexA] > array[randomIndexB]) {
  if (array[randomIndexB] > array[randomIndexC]) {
    return "b is the middle value";
  } else if (array[randomIndexA] > array[randomIndexC]) {
    return "c is the middle value";
  } else {
    return "a is the middle value";
  }
} else {
  if (array[randomIndexA] > array[randomIndexC]) {
    return "a is the middle value";
  } else if (array[randomIndexB] > array[randomIndexC]) {
    return "c is the middle value";
  } else {
    return "b is the middle value";
  }
}

这种方法需要至少两次和最多三个比较。它故意忽略了两个值相等的可能性(就像您的问题一样):如果这很重要,则可以扩展该方法以检查这一点。

于 2009-10-17T16:05:19.840 回答
21

这最多可以通过两次比较来完成。

int median(int a, int b, int c) {
    if ( (a - b) * (c - a) >= 0 ) // a >= b and a <= c OR a <= b and a >= c
        return a;
    else if ( (b - a) * (c - b) >= 0 ) // b >= a and b <= c OR b <= a and b >= c
        return b;
    else
        return c;
}
于 2012-12-17T07:50:56.727 回答
14

还有一个想法。有三个数字{a,b,c}。然后:

middle = (a + b + c) - min(a,b,c) - max(a,b,c);

当然,我们必须记住数字限制......

于 2014-05-07T18:56:30.213 回答
7

以下是仅使用条件句的表达方式:

int a, b, c = ...
int middle = (a <= b) 
    ? ((b <= c) ? b : ((a < c) ? c : a)) 
    : ((a <= c) ? a : ((b < c) ? c : b));

编辑:

  1. @Pagas 发现的上述错误已得到修复。
  2. @Pagas 还指出,如果您只使用条件,则不能使用少于 5 个条件来执行此操作,但您可以使用临时变量或值交换来减少这种情况。
  3. 我要补充一点,很难预测纯条件或分配解决方案是否会更快。这可能取决于 JIT 有多好,但我认为条件版本对优化器来说更容易分析。
于 2009-10-17T15:08:13.893 回答
6

我没有看到实现交换的解决方案:

int middle(int a, int b, int c) {
    // effectively sort the values a, b & c
    // putting smallest in a, median in b, largest in c

    int t;

    if (a > b) {
        // swap a & b
        t = a;
        a = b;
        b = t;
    }

    if (b > c) {
        // swap b & c
        t = b;
        b = c;
        c = t;

        if (a > b) {
            // swap a & b
            t = a;
            a = b;
            b = t;
        }
    }

    // b always contains the median value
    return b;
}
于 2014-10-19T19:44:51.123 回答
4

碰到一个旧线程,但它仍然是最短的解决方案,没有人提到它。

解决方案:

int median2(int a, int b, int c) {
    return (a > b) ^ (a > c) ? a : (a > b) ^ (b > c) ? c : b;
}

测试:

(测试涵盖所有可能的组合,所有组合都打印 6)

public static void main(String[] args) {

    System.out.println(median(3, 6, 9));
    System.out.println(median(3, 9, 6));
    System.out.println(median(6, 3, 9));
    System.out.println(median(6, 9, 3));
    System.out.println(median(9, 3, 6));
    System.out.println(median(9, 6, 3));
    System.out.println(median(6, 6, 3));
    System.out.println(median(6, 6, 9));
    System.out.println(median(6, 3, 6));
    System.out.println(median(6, 9, 6));
    System.out.println(median(3, 6, 6));
    System.out.println(median(9, 6, 6));
    System.out.println(median(6, 6, 6));

}

解释1

(a > b) ^ (a > c)c > a > b如果是或c < a < b- 返回则为假a

否则(a > b) ^ (b > c)为 falsea > b > c或者a < b < c- 返回 b;

否则返回 c;

解释2

让我们假设p = a > bq = b > c; s = a > c;

让我们建立一个卡诺图

   | 00  01  11  10 (p, q)
---+----------------------
 0 |  b   c   *   a
 1 |  *   a   b   c
(s)|

*表示组合是不可能的(如a > b; b > c; a < c

注意右边部分是镜像的左边部分,地图可以通过引入来简化t = p ^ q; u = s ^ p

   |  0   1 (t)
---+---------
 0 |  b   c  
 1 |  *   a  
(u)|

所以函数可以写成

private static int median(int a, int b, int c) {
    boolean t = (a > b) ^ (b > c);
    boolean u = (a > b) ^ (a > c);
    if (u)
        return a;
    else if (t)
        return c;
    else
        return b;
}

内联变量并用 ? 替换 ifs:给出答案

int median2(int a, int b, int c) {
    return (a > b) ^ (a > c) ? a : (a > b) ^ (b > c) ? c : b;
}

即使输入上的某些输入相等,该解决方案也能正常工作,这可能并不明显,但很合乎逻辑。

于 2018-04-11T19:06:02.870 回答
3

如果您必须从满足某些标准的 X 值中找到一个,您必须至少将该值与其他 X-1 个值进行比较。对于三个值,这意味着至少进行两次比较。由于这是“找到不是最小且不是最大的值”,因此您只需进行两次比较即可逃脱。

然后,您应该专注于编写代码,这样您就可以非常清楚地看到发生了什么并保持简单。这意味着嵌套的 if。这将允许 JVM 在运行时尽可能地优化这种比较。

请参阅 Tim 提供的解决方案(查找三元组中间值的最快方法?)以查看此示例。许多代码行不一定比嵌套问号冒号的代码大。

于 2009-10-17T16:46:52.243 回答
3

您不妨以最直接的方式编写此内容。正如你所说,只有六种可能性。没有任何合理的方法会更快或更慢,所以只需要一些易于阅读的东西。

为了简洁起见,我会使用 min() 和 max(),但我认为三个嵌套的 if/then 也一样好。

于 2009-10-17T18:59:42.443 回答
3

median = (a+b+c) - Math.min(Math.min(a,b),c) - Math.max(Math.max(a,b),c)

这是基本的,我不知道它的工作效率如何,但这些函数毕竟使用 if 条件。如果您愿意,可以将此语句转换为 if-else 语句,但这需要时间。为什么这么懒?

于 2014-09-28T18:45:23.353 回答
2

根据 Gyorgy 的出色回答,您可以通过将 min/max 替换为条件移动来获得没有分支的中值索引:

int i = (array[A] >= array[B]) ? A : B;
int j = (array[A] <= array[B]) ? A : B;
int k = (array[i] <= array[C]) ? i : C;
int median_idx = (array[j] >= array[k]) ? j : k;

javac 应该为这些三元赋值中的每一个生成一个 ConditionalNode,它cmp/cmov在汇编中转换为对。另请注意,选择比较是为了在相等的情况下返回按字母顺序排列的第一个索引。

于 2014-08-05T11:18:18.853 回答
2

最简单的方法是通过排序。例如考虑以下代码:

import java.util.Arrays;


int[] x = {3,9,2};
Arrays.sort(x); //this will sort the array in ascending order 

//so now array x will be x = {2,3,9};
//now our middle value is in the middle of the array.just get the value of index 1
//Which is the middle index of the array.

int middleValue = x[x.length/2]; // 3/2 = will be 1

就是这样,就这么简单。

这样你就不需要考虑数组的大小了。所以如果你有 47 个不同的值,那么你也可以使用这个代码来找到中间值。

于 2016-09-01T19:31:54.427 回答
1

这将起作用:

template<typename T> T median3_1_gt_2(const T& t1, const T& t2, const T& t3) {
    if (t3>t1) {
        return t1;
    } else {
        return std::max(t2, t3);
    }
}
template<typename T> T median3(const T& t1, const T& t2, const T& t3) {
    if (t1>t2) {
        return median3_1_gt_2(t1, t2, t3);
    } else {
        return median3_1_gt_2(t2, t1, t3);
    }
}

https://github.com/itroot/firing-ground/blob/864e26cdfced8394f8941c8c9d97043da8f998b4/source/median3/main.cpp

于 2013-03-05T15:46:22.737 回答
1
    if(array[aIndex] > array[bIndex]) {
        if(array[bIndex] > array[cIndex]) return bIndex;
        if(array[aIndex] > array[cIndex]) return cIndex;
        return aIndex;
    } else {
        if(array[bIndex] < array[cIndex]) return bIndex;
        if(array[aIndex] < array[cIndex]) return cIndex;
        return aIndex;
    }
于 2013-07-14T05:40:39.383 回答
1
largest=(a>b)&&(a>c)?a:(b>c?b:c);
smallest=(a<b)&&(a<c)?a:(b<c?b:c);
median=a+b+c-largest-smallest;
于 2014-11-28T22:25:26.943 回答
1

方法一

int a,b,c,result;
printf("enter three number");
scanf("%d%d%d",&a,&b,&c);
result=a>b?(c>a?a:(b>c?b:c)):(c>b?b:(a>c?a:c));
printf("middle %d",result);

方法二

int a=10,b=11,c=12;
//Checking for a is middle number or not
if( b>a && a>c || c>a && a>b )
{
    printf("a is middle number");
}

//Checking for b is middle number or not
if( a>b && b>c || c>b && b>a )
{
    printf("b is middle number");
}

//Checking for c is middle number or not
if( a>c && c>b || b>c && c>a )
{
    printf("c is middle number");
}

方法三

if(a>b)
{
    if(b>c)
    {
        printf("b is middle one");
    }
    else if(c>a)
    {
        printf("a is middle one");
    }
    else
    {
        printf("c is middle one");
    }
}
else
{
    if(b<c)
    {
        printf("b is middle one");
    }
    else if(c<a)
    {
        printf("a is middle one");
    }
    else
    {
        printf("c is middle one");
    }
}

我得到了适当的答案来找到三元组的中间值

于 2015-02-16T10:11:43.800 回答
1
// Compute median of three values, no branches

int median3(int V[3])
{
  unsigned int A,B,C;
  
  A=(V[0] < V[1]);
  B=(V[1] < V[2]);
  C=(V[0] < V[2]);

  return V[(B^C)<<1 | (A^B^1)];
  
}
于 2020-06-26T12:38:49.473 回答
0

在 ary 中使用 idxA 到 idxC,

int ab = ary[idxA] < ary[idxB] ? idxA : idxB;
int bc = ary[idxB] < ary[idxC] ? idxB : idxC;
int ac = ary[idxA] < ary[idxC] ? idxA : idxC;

int idxMid = ab == bc ? ac : ab == ac ? bc : ab;

indexMiddle 指向中间值。

说明:从3个最小值中,2个是整体最小值,其他值必须是中间值。因为我们检查相等性,所以我们可以比较最后一行的索引,而不必比较数组值。

于 2009-10-17T15:10:35.800 回答
0

您可以使用数组,如下所示:

private static long median(Integer i1, Integer i2, Integer i3) {

    List<Integer> list = Arrays.asList(
            i1 == null ? 0 : i1,
            i2 == null ? 0 : i2,
            i3 == null ? 0 : i3);

    Collections.sort(list);
    return list.get(1);
}
于 2014-05-09T12:32:55.860 回答
0

这是 Python 中的答案,但同样的逻辑也适用于 Java 程序。

def middleOfThree(a,b,c):
    middle = a
    if (a < b and b < c) or (c < b and b < a):
        middle = b 
    elif (a < c and c < b) or (b < c and c < a):
        middle = c    
    print 'Middle of a=%d, b=%d, c=%d is %d' % (a,b,c,middle)

middleOfThree(1,2,3)
middleOfThree(1,3,2)
middleOfThree(2,1,3)
middleOfThree(2,3,1)
middleOfThree(3,2,1)
middleOfThree(3,1,2)
于 2016-09-20T14:54:55.333 回答
0

整数的 100% 无分支版本:

int mid(const int a, const int b, const int c) {
    const int d0 = b - a;
    const int m = (d0 >> 31);
    const int min_ab = a + (d0 & m);
    const int max_ab = a + (d0 & ~m);
    const int d1 = c - max_ab;
    const int min_max_ab_c = max_ab + (d1 & (d1 >> 31));
    const int d2 = min_ab - min_max_ab_c;
    return min_ab - (d2 & (d2 >> 31));
}

使用无分支最小/最大函数构造:

int min(const int a, const int b) { const int d = b - a; return a + (d & (d >> 31)); }
int max(const int a, const int b) { const int d = a - b; return a - (d & (d >> 31)); }

它可能看起来不漂亮,但机器代码在某些架构上可能会更有效。特别是那些没有最小/最大指令的。但我没有做任何基准来确认它。

于 2017-07-26T14:04:05.797 回答
-1

或用于在包含中间值的数组中查找索引的单行:

 int middleIndex = (a[0]<a[1]) ? ((a[0]<a[2) ? a[2] : a[0]) : ((a[1]<a[2) ? a[2] : a[1]);
于 2009-10-17T15:10:38.520 回答
-1

其中很多似乎都在使用相当复杂的 if 语句。我使用数学库找到了一个非常简单的解决方法。

Math.max(Math.min(array[start], array[mid]), Math.min(array[start], array[mid], array[end]))

效果很好。

于 2017-05-08T20:39:03.640 回答
-1

用三元算子一行就可以解决

int middle(int A, int B, int C) {
      return (A>B&&A>C)?B>C?B:C:(B>C&&B>A)?A>C?A:C:B;
}
于 2020-11-17T08:41:01.137 回答