Python Forum

Full Version: In linear time, find the maximum sum of all possible contiguous subarrays
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2 3
Per an interview question offered as a "question of the day," how would you find, given an array of integers, the maximum sum of all possible contiguous subarrays in linear time. I'm pretty sure my solution is not in linear time, since I have a for-loop inside another for-loop:

def max_subarray_sum(arr):
    """Given an array of integers, find the maximum sum
    of all possible contiguous subarrays of the array"""
    # A variable to store the largest sum
    max_sum = 0
    # Loop through every possible contiguous subarray
    for mindex in range(0, len(arr)):
        for maxdex in range(mindex + 1, len(arr) + 1):
            # A variable to store the sum of the
            # integers in each subarray
            subarray_sum = 0
            # Add all the integers in the subarray together
            # and store the result in subarray_sum
            for each_int in arr[mindex:maxdex]:
                subarray_sum += each_int
            # If a subarray's sum is larger than the number
            # stored in the max_sum variable, update max_sum
            # with the new value
            if subarray_sum > max_sum:
                max_sum = subarray_sum
    # Return the largest sum found among all contiguous subarrays
    return max_sum

print(max_subarray_sum([34, -50, 42, 14, -5, 86]))
# Result: 137
# Correct Result: 137
False idea removed.
(Oct-01-2019, 11:37 PM)Gribouillis Wrote: [ -> ]False idea removed.

I don't understand your answer. Can you elaborate?

Update: Oh, wait. You gave an answer and then deleted it. Sorry, I'm dense sometimes. XD
I'm confused. The array as a whole is a subarray of the array, right? So the sum of all the positive numbers in the array is the maximum possible sum of all the possible subarrays, isn't it? Any other subarray made from removing a positive number or adding a negative number would have a smaller sum.
(Oct-02-2019, 01:05 AM)ichabod801 Wrote: [ -> ]I'm confused. The array as a whole is a subarray of the array, right? So the sum of all the positive numbers in the array is the maximum possible sum of all the possible subarrays, isn't it? Any other subarray made from removing a positive number or adding a negative number would have a smaller sum.

The keyword is "contiguous." The integers have to be next to each other in the array. You can't pick out only the positive integers.

Thus, the largest sum in the above example is the subarray [42, 14, -5, 86], which is 137. The sum of the entire array is smaller: 121.

Someone offered this, but I haven't been able to test or think through it yet:

def max_sum_contiguous(arr):
    running_totals = []
    running_total = 0
    for i, value in enumerate(arr):
        if value > -running_total:
            running_total += value
        else:
            running_total = 0
        running_totals.append(running_total)
    return max(running_totals)
It works. I tested the function that runs in linear time against my quadratic-time solution 10,000 times, and both give the same result, no matter what integers are in the array:

import random

# Given an array of integers, find the maximum sum
# of all possible contiguous subarrays of the array

def max_subarray_sum_quadratic(arr):
    max_sum = 0
    for mindex in range(0, len(arr)):
        for maxdex in range(mindex + 1, len(arr) + 1):
            subarray_sum = 0
            for each_int in arr[mindex:maxdex]:
                subarray_sum += each_int
            if subarray_sum > max_sum:
                max_sum = subarray_sum
    return max_sum

def max_subarray_sum_linear(arr):
    running_totals = []
    running_total = 0
    for value in arr:
        if value > -running_total:
            running_total += value
        else:
            running_total = 0
        running_totals.append(running_total)
    return max(running_totals)


for r in range(10000):
    whole_array = []
    for r2 in range(6):
        whole_array.append(random.randint(-100, 100))
    if max_subarray_sum_quadratic(whole_array) != max_subarray_sum_linear(whole_array):
        print("\nArray:", whole_array)
        print(max_subarray_sum_quadratic(whole_array), "quadratic")
        print(max_subarray_sum_linear(whole_array), "linear")
Now I just have to figure out how the linear-time function is working exactly. I'm slow at math.
It seems easy if you define s[j] = sum(arr[i] for i in range(j)) because if the maximum sum of contiguous subarrays is reached for a the range(a, b) of indices, with a <= b, then this sum is s[b] - s[a] and for all c <= b one has s[b] - s[c] <= s[b] - s[a] which means that s[a] <= s[c], hence s[a] is the minimum value of s for all indices smaller than b. Hence the algorithm

start with mv = 0 and sv = 0 and best = 0
for b in range(n):
    sv += arr[b]
    mv = min(mv, sv)
    best = max(best, sv - mv)
return best
(Oct-02-2019, 09:04 PM)Gribouillis Wrote: [ -> ]It seems easy if you define s[j] = sum(arr[i] for i in range(j)) because if the maximum sum of contiguous subarrays is reached for a the range(a, b) of indices, with a <= b, then this sum is s[b] - s[a] and for all c <= b one has s[b] - s[c] <= s[b] - s[a] which means that s[a] <= s[c], hence s[a] is the minimum value of s for all indices smaller than b. Hence the algorithm

start with mv = 0 and sv = 0 and best = 0
for b in range(n):
    sv += arr[b]
    mv = min(mv, sv)
    best = max(best, sv - mv)
return best

I didn't understand most of the that, but I'm going to work on it till I do!
Hey, this resource I found on Google might be helpful - https://www.interviewbit.com/blog/maximum-subarray-sum/
import random

def mymax(array):
    '''Find sub-array of array with the largest sum.
    Return (sum, start_index, end_index)
    '''
    subsum = start = 0
    maxsum = [subsum, start, start]
    for index, value in enumerate(array):
        subsum += value
        if subsum > maxsum[0]:
            # Found a new maximum
            maxsum = [subsum, start, index]
        elif subsum <= 0:
            # No future maxsums will contain anything from array[:index].  Look for max sub_array sum for array[i+1:]
            subsum = 0
            start = index + 1
    return maxsum

def gribmax(array):
    '''Solution from Griboullis for finding greatest sum of consecutive array values'''
    mv = sv = best = 0
    for value in array:
        sv += value
        mv = min(mv, sv)
        best = max(best, sv-mv)
    return best

x = random.choices(range(-100, 100), k=30)
print(x)
print(mymax(x))
print(gribmax(x))
My solution and Griboullis' solution agree when run on 100,000 different random arrays of length 100.
Pages: 1 2 3