5
\$\begingroup\$

The exercise 1.3 of the book Structure and Interpretation of Computer Programs asks the following:

Exercise 1.3. Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers.

My answer is this:

    var sumSquareLargest = function (x, y, z) {
        var numbers = [x, y, z];
        numbers.sort();
        return numbers[1] * numbers[1] + numbers[2] * numbers[2];
    };

Am I doing unnecessary work here? How could I improve this answer, even if only stylistically-speaking?

\$\endgroup\$
  • 2
    \$\begingroup\$ I have rolled back Rev 8 → 7. Please see What to do when someone answers. \$\endgroup\$ – 200_success Apr 7 '15 at 3:01
  • 1
    \$\begingroup\$ You don't really need to provide links to separate implementations. That's only necessary for follow-up posts. \$\endgroup\$ – Jamal Apr 7 '15 at 15:34
6
\$\begingroup\$

Finding the minimum of three numbers is easier than sorting three numbers, and it doesn't require creating a temporary array.

This runs about 10 times faster.

var sumSquareNotMin = function(x, y, z) {
    var min = Math.min(x, y, z);
    return -min * min + x * x + y * y + z * z;
};

As @Corbin mentioned, floating-point arithmetic is tricky. I've arranged the negative term first, which should cope better with overflow. However, in extremely unusual cases (such as when dealing with a mix of tiny and huge numbers), the result could differ from the original.

\$\endgroup\$
  • 1
    \$\begingroup\$ Well, I feel kind of silly now :). +1! Although... it's not going to realistically matter, but it might be worth noting that in extremely rare edge cases this is not necessarily equivalent due to the potential for overflow (this one is easily fixed) and floating point shenanigans (not quite so easily fixed). \$\endgroup\$ – Corbin Apr 7 '15 at 3:21
4
\$\begingroup\$

This looks very readable and clear which is great, but there is one subtle bug.

Array.sort is a little weird:

If compareFunction is not supplied, elements are sorted by converting them to strings and comparing strings in Unicode code point order. For example, "Cherry" comes before "banana". In a numeric sort, 9 comes before 80, but because numbers are converted to strings, "80" comes before "9" in Unicode order.

In other words, you maximum value finding logic is wrong:

sumSquareLargest(2, 3, 12); // 13 rather than expected 153 since "12" < "2" < "3"

Luckily the fix is easy. You just need to provide your own comparator:

var numbers = [x, y, z];
numbers.sort(function(a, b) { return a - b; });

Also, perhaps I'm just going through one of my functional kicks, but I would be tempted to utilize slice and reduce:

function sumSquareLargest(x, y, z) {
    return [x, y, z].sort(function(a, b) { return a - b; }).slice(1, 3)
                    .reduce(function(sum, x) { return sum + x*x; }, 0);
}

Or, with some bits pulled out to make it a bit clearer:

function numberLessThan(a, b) { return a - b; }

function addSquare(sum, sq) { return sum + sq*sq; }

function sumSquareLargest(x, y, z) {
    return [x, y, z].sort(numberLessThan).slice(1, 3).reduce(addSquare, 0);
}

On second thought, now that I've actually written this out and seen it, I think the simple approach is better. This just gets a bit too unwieldy. I'll leave it anyway as an option though, especially since it could be useful if you wanted to make a version that operated on any number of values instead of 3.

(Note: I've just declared my functions the way I have because it's the style I prefer in this situation. There's no meaningful difference.)

\$\endgroup\$
2
\$\begingroup\$

Based on the answer supplied by 200_success, here is an alternate version which goes slightly faster again (in Chrome at least).

http://jsperf.com/sumsquarelargest/6

It finds the max, and then the max of the remaining two. This means the square is only calculated for the two relevant maximums. This is not as large a difference as the original to the answer of 200_success, but still worth noting.

var sumSquareTwoMax = function(x, y, z) {
      var a = Math.max(x, y);
      var b = Math.max(Math.min(x, y), z);
      return a * a + b * b;
 };
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.