给定的是一个由三个数值组成的数组,我想知道这三个数值的中间值。
问题是,找到三者中间的最快方法是什么?
我的方法是这种模式 - 因为有三个数字,所以有六个排列:
if (array[randomIndexA] >= array[randomIndexB] &&
array[randomIndexB] >= array[randomIndexC])
如果有人能帮助我找到一种更优雅、更快捷的方法,那就太好了。
给定的是一个由三个数值组成的数组,我想知道这三个数值的中间值。
问题是,找到三者中间的最快方法是什么?
我的方法是这种模式 - 因为有三个数字,所以有六个排列:
if (array[randomIndexA] >= array[randomIndexB] &&
array[randomIndexB] >= array[randomIndexC])
如果有人能帮助我找到一种更优雅、更快捷的方法,那就太好了。
这里有一个使用 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
如果硬件可以在没有分支的情况下回答最小和最大查询(今天的大多数 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
这是正确的,因为:
应为 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 及更高版本)。
如果您正在寻找最有效的解决方案,我想它是这样的:
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";
}
}
这种方法需要至少两次和最多三个比较。它故意忽略了两个值相等的可能性(就像您的问题一样):如果这很重要,则可以扩展该方法以检查这一点。
这最多可以通过两次比较来完成。
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;
}
还有一个想法。有三个数字{a,b,c}
。然后:
middle = (a + b + c) - min(a,b,c) - max(a,b,c);
当然,我们必须记住数字限制......
以下是仅使用条件句的表达方式:
int a, b, c = ...
int middle = (a <= b)
? ((b <= c) ? b : ((a < c) ? c : a))
: ((a <= c) ? a : ((b < c) ? c : b));
编辑:
我没有看到实现交换的解决方案:
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;
}
碰到一个旧线程,但它仍然是最短的解决方案,没有人提到它。
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));
}
(a > b) ^ (a > c)
c > a > b
如果是或c < a < b
- 返回则为假a
;
否则(a > b) ^ (b > c)
为 falsea > b > c
或者a < b < c
- 返回 b;
否则返回 c;
让我们假设p = a > b
;q = 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;
}
即使输入上的某些输入相等,该解决方案也能正常工作,这可能并不明显,但很合乎逻辑。
如果您必须从满足某些标准的 X 值中找到一个,您必须至少将该值与其他 X-1 个值进行比较。对于三个值,这意味着至少进行两次比较。由于这是“找到不是最小且不是最大的值”,因此您只需进行两次比较即可逃脱。
然后,您应该专注于编写代码,这样您就可以非常清楚地看到发生了什么并保持简单。这意味着嵌套的 if。这将允许 JVM 在运行时尽可能地优化这种比较。
请参阅 Tim 提供的解决方案(查找三元组中间值的最快方法?)以查看此示例。许多代码行不一定比嵌套问号冒号的代码大。
您不妨以最直接的方式编写此内容。正如你所说,只有六种可能性。没有任何合理的方法会更快或更慢,所以只需要一些易于阅读的东西。
为了简洁起见,我会使用 min() 和 max(),但我认为三个嵌套的 if/then 也一样好。
median = (a+b+c) - Math.min(Math.min(a,b),c) - Math.max(Math.max(a,b),c)
这是基本的,我不知道它的工作效率如何,但这些函数毕竟使用 if 条件。如果您愿意,可以将此语句转换为 if-else 语句,但这需要时间。为什么这么懒?
根据 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
在汇编中转换为对。另请注意,选择比较是为了在相等的情况下返回按字母顺序排列的第一个索引。
最简单的方法是通过排序。例如考虑以下代码:
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 个不同的值,那么你也可以使用这个代码来找到中间值。
这将起作用:
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);
}
}
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;
}
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;
方法一
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");
}
}
我得到了适当的答案来找到三元组的中间值
// 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)];
}
在 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个是整体最小值,其他值必须是中间值。因为我们检查相等性,所以我们可以比较最后一行的索引,而不必比较数组值。
您可以使用数组,如下所示:
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);
}
这是 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)
整数的 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)); }
它可能看起来不漂亮,但机器代码在某些架构上可能会更有效。特别是那些没有最小/最大指令的。但我没有做任何基准来确认它。
或用于在包含中间值的数组中查找索引的单行:
int middleIndex = (a[0]<a[1]) ? ((a[0]<a[2) ? a[2] : a[0]) : ((a[1]<a[2) ? a[2] : a[1]);
其中很多似乎都在使用相当复杂的 if 语句。我使用数学库找到了一个非常简单的解决方法。
Math.max(Math.min(array[start], array[mid]), Math.min(array[start], array[mid], array[end]))
效果很好。
用三元算子一行就可以解决
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;
}