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

时间:2009-10-17 14:48:50

标签: java algorithm conditional logic median

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

问题是,找到三个中间最快方式是什么?

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

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

如果有人可以帮我找到更优雅的和更快的方式,那将是非常好的。

25 个答案:

答案 0 :(得分:76)

这里有一个答案,使用min / max和no branches(https://stackoverflow.com/a/14676309/2233603)。实际上4分钟/最大值操作足以找到中位数,不需要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

答案 1 :(得分: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

这是正确的,因为:

  • xor是可交换和关联的
  • xor on equal bits产生零
  • xor with zero不会改变位

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

在硬件不支持min / max的不太可能的情况下,可以这样做:

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

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

答案 2 :(得分: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";
  }
}

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

答案 3 :(得分: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;
}

答案 4 :(得分:12)

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

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

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

答案 5 :(得分: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的好坏,但我认为条件版本对于优化器来说更容易分析。

答案 6 :(得分:4)

我没有看到实施互换的解决方案:

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;
}

答案 7 :(得分:3)

你也可以用最简单的方式写这个。正如你所说,只有六种可能性。没有合理的方法可以更快或更慢,所以只需要一些易于阅读的东西。

我会使用min()和max()来表示简洁,但是我认为三个嵌套if / thens会一样好。

答案 8 :(得分:2)

如果必须找到满足某些标准的X值中的一个,则必须至少将该值与每个X-1其他值进行比较。对于三个值,这意味着至少两次比较。既然这是“找到不是最小而不是最大的价值”,那么你只需要进行两次比较即可获得。

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

请参阅Tim(Fastest way of finding the middle value of a triple?)提供的解决方案,以查看此示例。许多代码行不一定比嵌套的questionmark-colon更大。

答案 9 :(得分:2)

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

这是最基本的,我不知道这会有多高效,但这些功能毕竟是条件。如果您愿意,可以将此语句转换为if-else语句,但这需要时间。为什么这么懒?

答案 10 :(得分: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个不同的值,那么您也可以使用此代码来查找中间值。

答案 11 :(得分: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

答案 12 :(得分: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;
    }

答案 13 :(得分: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)];
  
}

答案 14 :(得分:1)

打破旧线程,但它仍然是最短的解决方案,没有人提及它。

解决方案:

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为false;

如果(a > b) ^ (b > c)a > b > c - 返回b;

,则

否则a < b < c为false

否则返回c;

解释2

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

让我们构建一个Karnaugh map

   | 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;
}

即使输入中的某些内容相等,解决方案也能正常工作,这可能不明显,但非常符合逻辑。

答案 15 :(得分:1)

根据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对。另请注意,选择的比较使得在相等的情况下,返回按字母顺序排列的第一个索引。

答案 16 :(得分: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;

答案 17 :(得分:1)

方法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);

方法2

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");
}

方法3

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");
    }
}

我得到了适当的finding the middle value of a triple

答案 18 :(得分:1)

以下是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)

答案 19 :(得分: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是总体最小值,另一个值必须是中间值。因为我们检查相等性,我们可以比较最后一行中的索引,而不是必须比较数组值。

答案 20 :(得分: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)); }

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

答案 21 :(得分: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);
}

答案 22 :(得分:-1)

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

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

非常好用。

答案 23 :(得分:-1)

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

 int middleIndex = (a[0]<a[1]) ? ((a[0]<a[2) ? a[2] : a[0]) : ((a[1]<a[2) ? a[2] : a[1]);

答案 24 :(得分:-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;
}