pythonclrs

Max subarray sum in CLRS (3rd edition) resulting in error


This is a continuation to my early complaints about CLRS(3rd edition) if you're looking to learn about algorithms, this is by far the worst book that you can ever use. All the pseudocode examples up to chapter 4 that I've seen so far have mistakes ranging from off by one, wrong/missing base cases for recursive functions, poor design choices that require complete redesign ex: the use of sentinel values in merge sort which is addressed in my other question I mentioned earlier. This book is overhyped and is plain garbage.

In the example shown below, the objective is to find a contiguous subarray with the largest sum.

ex: [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]

sequence: 18, 20, -7, 12: 43

ex: [4, 5]

sequence: 4, 5: 9

max-crossing-subarray

def max_crossing_subarray(a, low, mid, high):
    left_sum = -float('inf')
    total = 0
    max_left = None
    for i in range(mid, low - 1, -1):
        total += a[i]
        if total > left_sum:
            left_sum = total
            max_left = i
    right_sum = -float('inf')
    total = 0
    max_right = None
    for i in range(mid + 1, high):
        total += a[i]
        if total > right_sum:
            right_sum = total
            max_right = i
    return max_left, max_right, left_sum + right_sum

max-subarray

def max_subarray(a, low, high):
    if high - low == 1:  # `high == low` is wrong and results in recursion error
        return low, high, a[low]
    else:
        mid = (low + high) // 2
        left_low, left_high, left_sum = max_subarray(a, low, mid)
        right_low, right_high, right_sum = max_subarray(a, mid, high)
        cross_low, cross_high, cross_sum = max_crossing_subarray(a, low, mid, high)
        if left_sum >= right_sum and left_sum >= cross_sum:
            return left_low, left_high, left_sum
        if right_sum >= left_sum and right_sum >= cross_sum:
            return right_low, right_high, right_sum
        else:
            return cross_low, cross_high, cross_sum

It works for some examples: list size > 2, otherwise, fails:

s1 = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
s2 = [2]
s3 = [4, 5]
for s in s1, s2, s3:
    print(s)
    print(max_subarray(s, 0, len(s)))
    print(70 * '=')

Result:

[13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
(7, 10, 43)  # correct sum, inclusive indices are correct
======================================================================
[2]
(0, 1, 2)  # correct sum, exclusive indices are correct
======================================================================
[4, 5]
(1, 2, 5)  # all wrong
======================================================================

Question: code producing wrong/inconsistent results, what's wrong? why? how to fix?


Solution

  • The code given by the book treats low and high as inclusive indices. So firstly your call to the method should be

    print(max_subarray(s, 0, len(s)-1))
    

    There is no recursion issue with max_subarray() and the if condition should be left as if low == high:.

    The call for the right half of the subarray should have mid + 1

    right_low, right_high, right_sum = max_subarray(a, mid+1, high)
    

    And in max_crossing_subarray(), the loop condition should be for i in range(mid + 1, high + 1):.

    With all those changes made, the output becomes:

    [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
    (7, 10, 43)
    ======================================================================
    [2]
    (0, 0, 2)
    ======================================================================
    [4, 5]
    (0, 1, 9)
    ======================================================================