DEV Community

Discussion on: Write better code: Day 1 - Highest Product

Collapse
 
qm3ster profile image
Mihail Malo • Edited

I feel like much less comparisons can be done by keeping track of the lowest number:

def get_product_of_3(array_of_ints):
    maxes = [0, 0, 0]
    min_val = 0
    min_i = 0
    for int in array_of_ints:
        if int > min_val:
            maxes[min_i] = int
            min_i, min_val = min(enumerate(maxes), key=lambda x: x[1])

    return maxes[0]*maxes[1]*maxes[2]

Sorry, I replaced one language I don't know with a similar language I don't know because I didn't want to install ruby.

Collapse
 
arjunrajkumar profile image
Arjun Rajkumar

Thanks Mihail. This looks much more neat. Will test it out later.

But did you test the case if two negative numbers are passed?
For e.g [-5, -4, 1, 3, 2] -> should give 60.

Thread Thread
 
qm3ster profile image
Mihail Malo • Edited

Yes, I see the problem.
So far I see three cases:

  • The biggest product is all positives.
  • The biggest product is two smallest negatives and one biggest positive.
  • The biggest product is negative itself(!), which can happen in at least these two distinct cases:
    • [n,p,p]: There are only 3 inputs, one of them negative. For any length 3 input we should just return the input anyway, implicitly or explicitly.
    • [n,n,n...]: There are 3 or more inputs, none of which are positive. In this case we should return the 3 negative numbers closest to 0 (3 biggest numbers, same as the all-positive happy case)

I'll post a fix later, let's see how messy.

Thread Thread
 
qm3ster profile image
Mihail Malo • Edited

Okay, I think I got it:

import math


def get_product_of_3(array_of_ints):
    if len(array_of_ints) < 3:
        return 0

    min_max = -math.inf
    min_max_i = 0
    maxes = [min_max, min_max, min_max]
    max_neg = 0
    max_neg_i = 0
    negs = [0, 0]
    for int in array_of_ints:
        if int > min_max:
            maxes[min_max_i] = int
            min_max_i, min_max = min(enumerate(maxes), key=lambda x: x[1])
        if int < max_neg:
            negs[max_neg_i] = int
            max_neg_i, max_neg = max(enumerate(negs), key=lambda x: x[1])

    prod_maxes = maxes[0]*maxes[1]*maxes[2]
    prod_negs = negs[0]*negs[1]
    # This condition is only needed for `[n,p,p]`, to not return the invalid product of `0` when the product of maxes is negative. 
    if prod_negs != 0:
        return max(prod_negs*max(maxes), prod_maxes)
    return prod_maxes

If we instead explicitly return the product of 3 inputs, this condition is no longer needed:

import math


def get_product_of_3(array_of_ints):
    if len(array_of_ints) < 3:
        return 0
    if len(array_of_ints) == 3:
        return array_of_ints[0]*array_of_ints[1]*array_of_ints[2]

    min_max = -math.inf
    min_max_i = 0
    maxes = [min_max, min_max, min_max]
    max_neg = 0
    max_neg_i = 0
    negs = [0, 0]
    for int in array_of_ints:
        if int > min_max:
            maxes[min_max_i] = int
            min_max_i, min_max = min(enumerate(maxes), key=lambda x: x[1])
        if int < max_neg:
            negs[max_neg_i] = int
            max_neg_i, max_neg = max(enumerate(negs), key=lambda x: x[1])

    prod_maxes = maxes[0]*maxes[1]*maxes[2]
    prod_negs = negs[0]*negs[1]
    return max(prod_negs*max(maxes), prod_maxes)

Another possible optimization, for input such as [...-100,-99,-98,-97,1,-96,-95,-94...] is to stop expecting the "all negatives" case. This can easily be done by resetting all negative maxes to 0, so that incoming negatives are always smaller:

import math


def get_product_of_3(array_of_ints):
    if len(array_of_ints) < 3:
        return 0
    if len(array_of_ints) == 3:
        return array_of_ints[0]*array_of_ints[1]*array_of_ints[2]

    awaiting_not_negatve = True
    min_max = -math.inf
    min_max_i = 0
    maxes = [min_max, min_max, min_max]
    max_neg = 0
    max_neg_i = 0
    negs = [0, 0]
    for int in array_of_ints:
        if int > min_max:
            maxes[min_max_i] = int
            if awaiting_not_negatve and int >= 0:
                awaiting_not_negatve = False
                for i, v in enumerate(maxes):
                    if v < 0:
                        maxes[i] = 0
            min_max_i, min_max = min(enumerate(maxes), key=lambda x: x[1])
        if int < max_neg:
            negs[max_neg_i] = int
            max_neg_i, max_neg = max(enumerate(negs), key=lambda x: x[1])

    prod_maxes = maxes[0]*maxes[1]*maxes[2]
    prod_negs = negs[0]*negs[1]
    return max(prod_negs*max(maxes), prod_maxes)