views:

441

answers:

7

(pseudo code)

function highest(i, j, k)
{

  if(i > j && i > k)
  {
    return i;
  }
  else if (j > k)
  {
    return j;
  }
  else
  {
    return k;
  }
}

I think that works, but is that the most efficient way?

+21  A: 

To find the greatest you need to look at exactly 3 ints, no more no less. You're looking at 6 with 3 compares. You should be able to do it in 3 and 2 compares.

int ret = max(i,j);
ret = max(ret, k);
return ret;
Chris H
Could make a nice little function: `template <typename T> const T }`
GMan
`max(i, max(j, k))` saves it to one line. I like the template version too
Nick Bedford
the stl defines that, doesn't it? Would be a good thing to use.
Chris H
Nice and concise answer.
Preet Sangha
Nick Bedford has the right idea.
Jasarien
Of course, if `max` is a macro or an inline function, `max(i, max(j, k))` is going to expand to something along the lines of `i > ( j > k ? j : k ) ? i : ( j > k ? j : k )` (CSE optimization notwithstanding) and if it isn't, you have function call overhead. Are we talking about programmer efficiency or computational efficiency?
Duncan
You're assuming that `max` is built into the hardware at the same cost as a comparison. What machine are you using?
Norman Ramsey
@Duncan: You're wrong about when it's an inline function, and since this question is C++, we're talking about std::max. A macro named 'max' is, in C++, pure abomination.
Roger Pate
@Roger Pate: Macros are an abomination even in C :). Perhaps I should have said "..a template or inline ...." So what happens for an inline expression? Surely that will inline to something like `rv1 = j > k ? j : k; rv2 = i > rv1 ? i : rv1; return rv2;` which is the same thing only with CSE. Have I missed something? Perhaps things have changed in the 2 decades since I last bothered looking at generated assembly language. I don't mind being told I'm wrong, though I do prefer to be told why :)
Duncan
@Duncan, @Chris: let's call it `std::max` and be through with it. @Chris: GNU STL defines median as a private function, maybe that's what you're thinking of.
Potatoswatter
But isn't accessing ret in the second line considered another "looking", which makes it 4 instead of 3?
jasonline
@Duncan: I interpreted you as using macro expansion in both and relying on CSE to clean it up, versus how the semantics of inline functions (even inline functions in C) mean CSE doesn't even apply (you no longer have a common expression); and I can see how you meant other than what I read earlier.
Roger Pate
I think Ignacio's code was more straight forward, but your explanation at the top takes the cake. Well put.
Stephano
+10  A: 

Pseudocode:

result = i
if j > result:
  result = j
if k > result:
  result = k
return result
Ignacio Vazquez-Abrams
I like that you only used if statements. This is a clean answer.
Stephano
Excellent! Very clear, and makes it very likely that if the hardware has predicated instructions like `cmov`, the compiler will use them. +1 (wish I could +10 past the evil `max`)
Norman Ramsey
Great answer. As simple as it could get with no attempt at being clever. +1 for humility.
IV
+7  A: 

How about

return i > j? (i > k? i: k): (j > k? j: k);

two comparisons, no use of transient temporary stack variables...

Charles Bretana
+1. Nifty. Mesmerizing and hurtful to the eyes at the same time.
Skurmedel
+1 for *not* doing that as a `#define`. ^_^
Mike DeSimone
+1 for being the first person to mention the one-line solution. i may loath the idea of trying to make everything shorter, but every now and again someone shows me a 1-liner that is quite impressive.
Stephano
+4  A: 

Your current method: http://ideone.com/JZEqZTlj (0.40s)

Chris's solution:

int ret = max(i,j);
ret = max(ret, k);
return ret;

http://ideone.com/hlnl7QZX (0.39s)

Solution by Ignacio Vazquez-Abrams:

result = i;
if (j > result)
  result = j;
if (k > result)
  result = k;
return result;

http://ideone.com/JKbtkgXi (0.40s)

And Charles Bretana's:

return i > j? (i > k? i: k): (j > k? j: k);

http://ideone.com/kyl0SpUZ (0.40s)

Of those tests, all the solutions take within 3% the amount of time to execute as the others. The code you are trying to optimize is extremely short as it is. Even if you're able to squeeze 1 instruction out of it, it's not likely to make a huge difference across the entirety of your program (modern compilers might catch that small optimization). Spend your time elsewhere.

EDIT: Updated the tests, turns out it was still optimizing parts of it out before. Hopefully it's not anymore.

Wallacoloo
That's why I said it won't make much of a difference, removing 1 of those millions won't do you much good.
Wallacoloo
ok, I'm (slightly) happier with the updated version. SO won't let me remove the -1 though ("Vote too old to be changed, unless this answer is edited"), sorry.
sfussenegger
Wow, sweet. I had no idea ideone existed. this gives me something to do when I can't sleep :) .
Stephano
+2  A: 

Not sure if this is the most efficient or not, but it might be, and it's definitely shorter:

int maximum = max( max(i, j), k);
Scott Smith
+4  A: 

For a question like this, there is no substitute for knowing just what your optimizing compiler is doing and just what's available on the hardware. If the fundamental tool you have is binary comparison or binary max, two comparisons or max's are both necessary and sufficient.

I prefer Ignacio's solution:

result = i;
if (j > result)
  result = j;
if (k > result)
  result = k;
return result;

because on the common modern Intel hardware, the compiler will find it extremely easy to emit just two comparisons and two cmov instructions, which place a smaller load on the I-cache and less stress on the branch predictor than conditional branches. (Also, the code is clear and easy to read.) If you are using x86-64, the compiler will even keep everything in registers.

Note you are going to be hard pressed to embed this code into a program where your choice makes a difference...

Norman Ramsey
+4  A: 

I like to eliminate conditional jumps as an intellectual exercise. Whether this has any measurable effect on performance I have no idea though :)

#include <iostream>
#include <limits>

inline int max(int a, int b)
{
    int difference = a - b;
    int b_greater = difference >> std::numeric_limits<int>::digits;
    return a - (difference & b_greater);
}

int max(int a, int b, int c)
{
    return max(max(a, b), c);
}

int main()
{
    std::cout << max(1, 2, 3) << std::endl;
    std::cout << max(1, 3, 2) << std::endl;
    std::cout << max(2, 1, 3) << std::endl;
    std::cout << max(2, 3, 1) << std::endl;
    std::cout << max(3, 1, 2) << std::endl;
    std::cout << max(3, 2, 1) << std::endl;
}

This bit twiddling is just for fun, the cmov solution is probably a lot faster.

FredOverflow