-1

Is this one-liner correct for calculating the middle int between a, b and c?

Note: I don't want to calculate the mean, just the one that's in the middle if these 3 were ordered from smallest to greatest (median).

Example: the middle value of 1, 77 and 34 is 34.

public static int midpoint(int a, int b, int c) {

    return a <= b ? (c <= a ? a : c <= b ? c : b ) : c <= b ? b : c <= a ? c : a;
}
j1nma
  • 447
  • 3
  • 7
  • 24
  • so is your code working or not? – Scary Wombat Apr 24 '17 at 02:24
  • 1
    It is... but few tests were made... – j1nma Apr 24 '17 at 02:24
  • I'm looking for a better solution (and correct in the case mine is wrong). – j1nma Apr 24 '17 at 02:25
  • 2
    "Code works" == https://codereview.stackexchange.com/tour – OneCricketeer Apr 24 '17 at 02:25
  • 1
    If you are looking for a better solution than you must look here http://stackoverflow.com/questions/1582356/fastest-way-of-finding-the-middle-value-of-a-triple – Flood2d Apr 24 '17 at 02:27
  • 3
    What do you mean "few tests were made"? There are 6 possible orders: `a < b < c`, `a < c < b`, `b < a < c`, `b < c < a`, `c < a < b`, `c < b < a`. If all six of those cases work, your code is fine. – ajb Apr 24 '17 at 02:33
  • 1
    @ajb exhaustive testing might require a bit more than 6 cases since it is conceivable that e.g. a bug might show up in `a = b < c` but not in `a < b < c` The distinction between ` – John Coleman Apr 24 '17 at 03:19
  • 1
    Possible duplicate of [Fastest way of finding the middle value of a triple?](http://stackoverflow.com/questions/1582356/fastest-way-of-finding-the-middle-value-of-a-triple) – andand Apr 24 '17 at 04:05
  • 2
    You said "I don't want to calculate the median ... just the one that's in the middle..." That's pretty much the definition of median. – andand Apr 24 '17 at 04:07

3 Answers3

3

I tried three various options and counted time taken by each one of them in the iteration of 1,000,000

  1. Using complex ternary
  2. Using Collections sort method with List
  3. Using Math min and max

All of them give same result but 2 and 3 are executed in almost zero milliseconds

Performance Test

int iterationCount = 1000000; // 1 million iterations
        Long t1 = Calendar.getInstance().getTimeInMillis();
        for (int i = 0; i < iterationCount; i++) {
            middle = a <= b ? (c <= a ? a : c <= b ? c : b) : c <= b ? b : c <= a ? c : a;
        }
        Long t2 = Calendar.getInstance().getTimeInMillis();


        // #2 Using Collection + List + Sort
        Long t3 = Calendar.getInstance().getTimeInMillis();
        for (int i = 0; i < iterationCount; i++) {
            List<Integer> list = Arrays.asList(a, b, c);
            Collections.sort(list);
            middle = list.get(1);
        }
        Long t4 = Calendar.getInstance().getTimeInMillis();

        // #3Using Math's min and max
        Long t5 = Calendar.getInstance().getTimeInMillis();
        for (int i = 0; i < iterationCount; i++) {
            middle = Math.max(Math.min(a, b), Math.min(Math.max(a, b), c));
        }
        Long t6 = Calendar.getInstance().getTimeInMillis();

        System.out.println("Time Taken #1 -> " + (t2 - t1));
        System.out.println("Time Taken #2 -> " + (t4 - t3));
        System.out.println("Time Taken #3 -> " + (t6 - t5));

Output

Time Taken #1 -> 16
Time Taken #2 -> 71
Time Taken #3 -> 6

Looking at the output the option #3 is the best in terms of the performance.

Best of three

 middle = Math.max(Math.min(a, b), Math.min(Math.max(a, b), c));
Amit Phaltankar
  • 2,859
  • 2
  • 17
  • 31
  • 1
    Your "benchmarking" measures only 1 iteration of a calculation and includes the call to `System.out.println()`. Fix it and I'll remove my downvote. – Jason S Apr 24 '17 at 02:51
  • 1
    better, thanks. Although even 100 is too short. Try 100,000? :-) – Jason S Apr 24 '17 at 04:58
  • @JasonS tried with 1 million iterations and that actually highlighted the difference :-) thanks for the suggestion – Amit Phaltankar Apr 24 '17 at 05:12
1

Is this one-liner correct?

Let's step through it:

return a <= b ? (c <= a ? a : c <= b ? c : b ) : c <= b ? b : c <= a ? c : a;

suppose a <= b. Then if c <= a, return a. Thus, c <= a <= b and a is the correct thing to return. On the other hand, if c <= b, then c <= b < a (b/c we know that c <= a is not true). So, b is appropriate to return.

Now suppose a <= b is false - then b > a. Now if c <= b, we have c <= b < a, so b is correct. If c <= b is false, then we have c > b and b > a so we will return a. Notice that you have an extra comparison. (we already know that b > a, and we've just learned that c > b, which gives us the ordering)

Notice two things about this one-liner: while it turned out that your logic was correct, it was difficult and time-consuming to determine that correctness, and it doesn't scale (imagine this one-liner for five items...)

The correct approach is to sort the list and take the middle item.

(which is exactly what you asked for: "just the one that's in the middle if these 3 were ordered from smallest to greatest.")

Jon Kiparsky
  • 6,854
  • 2
  • 20
  • 37
0
Math.max(Math.min(a, b), Math.min(Math.max(a, b), c));

Works for all cases.

j1nma
  • 447
  • 3
  • 7
  • 24