在不使用条件语句和三元运算符的情况下,在C中最多找到三个数字

时间:2011-08-16 05:38:46

标签: c algorithm conditional-statements

我必须找到最多由用户提供的三个号码,但有一些限制。不允许使用任何条件声明。我尝试使用下面的三元运算符。

max=(a>b?a:b)>c?(a>b?a:b):c

但它再次限制使用三元运算符。 现在我不知道该怎么做?

13 个答案:

答案 0 :(得分:69)

利用布尔表达式中的短路:

int max(int a, int b, int c)
{
     int m = a;
     (m < b) && (m = b); //these are not conditional statements.
     (m < c) && (m = c); //these are just boolean expressions.
     return m;
}

说明:

在布局AND操作(例如x && y)中,当且仅当 x为真时,才会评估y 。如果x为false,则不会评估y,因为整个表达式都是false,可以在不评估y的情况下推断出来。当可以推导出布尔表达式的值而不评估其中的所有操作数时,这称为短路。

将此原则应用于上述代码。最初ma。现在,如果(m < b)为真,则表示b大于m(实际上是a),因此评估第二个子表达式(m = b)m设置为b。但是,如果(m < b)为false,则不会评估第二个子表达式,m将保持a(大于b)。以类似的方式,评估第二个表达式(在下一行)。

简而言之,您可以按如下方式阅读(m < x) && (m = x)表达式:将m设置为x 当且仅当 m小于x(m < x)为真。希望这有助于您理解代码。

测试代码:

int main() {
        printf("%d\n", max(1,2,3));
        printf("%d\n", max(2,3,1));
        printf("%d\n", max(3,1,2));
        return 0;
}

<强>输出:

3
3
3

请注意max的实现会发出警告,因为未使用已计算的表达式:

  

prog.c:6:警告:未使用计算的值
  prog.c:7:警告:未使用计算值

要避免这些(无害的)警告,您可以将max实现为:

int max(int a, int b, int c)
{
     int m = a;
     (void)((m < b) && (m = b)); //these are not conditional statements.
     (void)((m < c) && (m = c)); //these are just boolean expressions.
     return m;
}

诀窍是现在我们casting the boolean expressions to void, which causes suppression of the warnings

答案 1 :(得分:19)

假设你正在处理整数,那么:

#define max(x,y) (x ^ ((x ^ y) & -(x < y)))
int max3(int x, int y, int z) {
    return max(max(x,y),z);
}

答案 2 :(得分:15)

只是为了避免条件执行添加另一种替代方法(这不是我会使用的,但似乎在解决方案集中缺失):

int max( int a, int b, int c ) {
   int l1[] = { a, b };
   int l2[] = { l1[ a<b ], c };
   return l2[ l2[0] < c ];
}

该方法使用(和大多数其他方法一样),当转换为int时,布尔表达式的结果产生0或1.两个值的简化版本将是:

int max( int a, int b ) {
   int lookup[] { a, b };
   return lookup[ a < b ];
}

如果表达式a<b正确,我们返回b,小心地存储在查找数组的第一个索引中。如果表达式产生false,那么我们返回a,它存储为查找数组的元素0。使用它作为构建块,您可以说:

int max( int a, int b, int c ) {
   int lookup[ max(a,b), c ];
   return lookup[ max(a,b) < c ];
}

通过使用已存储在max中的结果并内联原始调用lookup[0]来避免对内部max(int,int)的第二次调用,可以轻松转换为上述代码。


(这部分只是另一个证据,你必须先测量才能得出结论,最后看编辑)

至于我实际使用哪个......好吧,可能@Foo Baa here修改后使用内联函数而不是宏。下一个选项可以是这一个,也可以是@MSN here

这三个解决方案在公认答案中没有出现的共同点是它们不仅避免了if或三元运算符?:的句法结构,而且避免了分支,这可能会对性能产生影响。当没有分支时,CPU中的分支预测器不可能错过。


在考虑表现时,首先要考虑

我实际上为双向最大值实现了一些不同的选项,并由编译器分析生成的代码。以下三个解决方案生成所有相同的汇编代码:

int max( int a, int b ) { if ( a < b ) return b; else return a; }
int max( int a, int b ) { return (a < b? b : a ); }
int max( int a, int b ) {
   (void)((a < b) && (a = b));
   return a;
}

这并不奇怪,因为这三者都代表完全相同的操作。有趣的信息是生成的代码不包含任何分支。使用cmovge指令(在intel x64平台中使用g ++进行测试)实现很简单:

movl    %edi, %eax       # move a into the return value
cmpl    %edi, %esi       # compare a and b
cmovge  %esi, %eax       # if (b>a), move b into the return value
ret

技巧在条件移动指令中,避免任何潜在的分支。

其他解决方案都没有任何分支,但所有这些分支都转换为比任何分支更多的cpu指令,这些指令在一天结束时向我们保证我们应该始终编写简单的代码和让编译器为我们优化它。

答案 3 :(得分:6)

更新: 4年之后,我看到如果两个或多个值恰好相同,它会失败。用>替换>=会改变行为,但不能解决问题。它可能仍然可以挽救,所以我不会删除它,但不要在生产代码中使用它。


好的,这是我的:

int max3(int a, int b, int c)
{
    return a * (a > b & a > c) +
           b * (b > a & b > c) +
           c * (c > a & c > b);
}

请注意,使用&而不是&&可以避免任何条件代码;它依赖于>始终产生0或1的事实。(为a > b生成的代码可能涉及条件跳转,但它们在C中不可见。)

答案 4 :(得分:4)

int fast_int_max(int a, int b)
{
    int select= -(a < b);
    unsigned int b_mask= select, a_mask= ~b_mask;

    return (a&a_mask)|(b&b_mask);
}

int fast_int_max3(int a, int b, int c)
{
    return fast_int_max(a, fast_int_max(b, c));
}

答案 5 :(得分:3)

布尔值运算符(包括&lt;,&amp;&amp;等)通常会转换为机器代码级别的条件运算,因此不能满足挑战的精神。这是一个解决方案,任何合理的编译器都只能转换为没有条件跳转的算术指令(假设long有比int更多的位,而long是64位)。该想法是“m”捕获并复制b-a的符号位,因此m或者全是1位(如果a> b)或者是全0位(如果a <= b)。请注意,long用于避免溢出。如果由于某种原因你知道b - a没有过度/不足,那么就不需要使用long。

int max(int a, int b)
{
    long d = (long)b - (long)a;
    int m = (int)(d >> 63);
    return a & m | b & ~m;
}

int max(int a, int b, int c)
{
    long d;
    int m;
    d = (long)b - (long)a;
    m = (int)(d >> 63);
    a = a & m | b & ~m;
    d = (long)c - (long)a;
    m = (int)(d >> 63);
    return a & m | c & ~m;
}

答案 6 :(得分:2)

没有条件。只有一个铸造到uint。完美的解决方案。

int abs (a) { return (int)((unsigned int)a); }
int max (a, b) { return (a + b + abs(a - b)) / 2; }
int min (a, b) { return (a + b - abs(a - b)) / 2; }


void sort (int & a, int & b, int & c)
{
   int max = max(max(a,b), c);
   int min = min(min(a,b), c);
   int middle = middle = a + b + c - max - min;
   a = max;
   b = middle;
   c = min;
}

答案 7 :(得分:1)

您可以使用此代码查找两个中最大的一个:

max{a,b} = abs(a-b)/2 + (a+b)/2

然后再次使用它来找到第三个数字:

max{a,b,c} = max(a,max(b,c))

看到这适用于正数,你可以将其改为负数。

答案 8 :(得分:0)

#include "stdafx.h"
#include <iostream>
int main()
{       
        int x,y,z;
        scanf("%d %d %d", &x,&y, &z);
        int max = ((x+y) + abs(x-y)) /2;
        max = ((max+z) + abs(max-z)) /2;
        printf("%d ", max);
        return 0;
}            

答案 9 :(得分:0)

conditional statements,只需加载和分配。与其他人的答案完全不同:))

while (a > b)
{
    while (a > c)
    {
        tmp = a;
        goto finish;
    }
    tmp = c;
    goto finish;
}
while (b > c)
{
    tmp = b;
    goto finish;
}
tmp = c;
finish: max = tmp;

答案 10 :(得分:-1)

int compare(int a,int b, intc)
{
    return (a > b ? (a > c ? a : c) : (b > c ? b : c))
}

答案 11 :(得分:-1)

试试这个。

#include "stdio.h"
main() {
    int a,b,c,rmvivek,arni,csc; 
    printf("enter the three numbers");
    scanf("%d%d%d",&a,&b,&c);
    printf("the biggest value is %d",(a>b&&a>c?a:b>c?b:c));
}

答案 12 :(得分:-2)

max =  a > b ? ( a > c ? a : c ) : ( b > c ? b : c ) ;