找到三重中间值的最快方法?

Gna*_*ark 42 java algorithm logic conditional median

给定是一个包含三个数值的数组,我想知道这三个数值的中间值.

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

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

if (array[randomIndexA] >= array[randomIndexB] &&
    array[randomIndexB] >= array[randomIndexC])
Run Code Online (Sandbox Code Playgroud)

如果有人可以帮助我找到更优雅,更快捷的方法,那将是非常好的.

Gyo*_*ely 78

这里有一个使用min/max和没有分支的答案(/sf/answers/1027341661/).实际上4分钟/最大操作足以找到中位数,不需要xor:

median = max(min(a,b), min(max(a,b),c));
Run Code Online (Sandbox Code Playgroud)

虽然,它不会给你中值的索引......

所有案件的细分:

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
Run Code Online (Sandbox Code Playgroud)

  • 洞察这个答案:当你有一个函数 `clamp(x,L,H) = max(L,min(H,x))` 时,3 的中位数是 `clamp(c, min(a,b),最大(a,b))`。 (4认同)

Max*_*Max 35

如果硬件可以在没有分支的情况下回答最小和最大查询(现在大多数CPU可以执行此操作),则可以在没有分支的情况下回答查询.

运算符^表示按位xor.

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
Run Code Online (Sandbox Code Playgroud)

这是正确的,因为:

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

应为int/float选择适当的最小/最大函数.如果只存在正浮点数,那么可以直接在浮点表示上使用整数最小值/最大值(这可能是合乎需要的,因为整数运算通常更快).

在不太可能出现硬件不支持min/max的情况下,可以执行以下操作:

max(a,b)=(a+b+|a-b|)/2
min(a,b)=(a+b-|a-b|)/2
Run Code Online (Sandbox Code Playgroud)

但是,使用浮点运算时这是不正确的,因为需要精确的最小值/最大值而不是接近它的值.幸运的是,浮动最小/最大值已经硬件支持多年(在x86上,从奔腾III及以后).

  • 它只是通过使用绝对值来扩展最小和最大函数.| AB | 表示ab的绝对值.无论哪种方式,我都建议Gyorgy(http://stackoverflow.com/a/19045659/2037811)给出的答案比我的更整洁. (2认同)

Tim*_*Tim 25

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

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";
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这有点难看,我认为OP正在寻找一个优雅的解决方案.诀窍是很多人将更少的字符误认为更优雅,而实际上,编译器/虚拟机更容易优化更简单(这个答案). (2认同)
  • 即使这段代码是 18 行;这是有效的。将它放在一个函数中,并在需要时简单地调用它。 (2认同)

Zac*_*onn 20

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

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,这很容易出现整数溢出.由于这个原因,我不会在生产中推荐它. (2认同)

jac*_*ach 13

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

middle = (a + b + c) - min(a,b,c) - max(a,b,c);
Run Code Online (Sandbox Code Playgroud)

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

  • 它更像是一个_idea_如何解决问题,而不是确切的解决方案 (5认同)
  • @Celeritas`min(a,b,c)= min(a,min(b,c))` (4认同)
  • 不要明白。Java 没有接受 3 个参数的 `min()` 或 `max()`。 (2认同)

Ste*_*n C 7

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

int a, b, c = ...
int middle = (a <= b) 
    ? ((b <= c) ? b : ((a < c) ? c : a)) 
    : ((a <= c) ? a : ((b < c) ? c : b));
Run Code Online (Sandbox Code Playgroud)

EDITS:

  1. @Pagas发现的上述错误已得到修复.
  2. @Pagas还指出,如果仅使用条件,则不能使用少于5个条件,但您可以使用临时变量或值交换来减少此条件.
  3. 我想补充一点,很难预测纯条件解决方案或分配解决方案是否会更快.它可能取决于JIT的好坏,但我认为条件版本对于优化器来说更容易分析.

  • 你不能避免至少有5个条件,除非你做值交换或递归.这是因为相应的决策树有6个叶子,这意味着5个内部节点,因此整个代码中有5个决策点,尽管它们中一次只有两个或三个是活动的,那些是在答案叶子的路径中.但也许可以通过使用交换或其他技术来减少代码的大小,或者至少是条件的数量! (2认同)

mck*_*mey 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;
}
Run Code Online (Sandbox Code Playgroud)