#=======================================================================
# Author: Isai Damier
# Title: Maximum Sub-Array Sum
# Project: geekviewpoint
# Package: algorithms
#
# Statement:
# Given an array of integers (possibly positive and negative),
# find the sub-array whose sum is maximum. The sub-array must have
# at least two elements.
# Note: a sub-array is a continuous segment of an array.
#
# Time-complexity: O(n)
#
# Sample Input: {-1, 2, -3, 12, 23, -4, -5}
# Sample Output: {12, 23}
#
# Dynamic Programming Strategy:
#
# Let's call the input array X. Then an easy solution to this problem
# proceeds as follows:
#
# 0] scan X from left to right, tracking the sum of the maximal
# sub-array that ends at index i.
# 1] scan X from right to left, tracking the sum of the maximal
# sub-array that ends at index j.
# 2] find the maximum starting and ending indices (i.e. iMax and jMax)
# 3] then answer is the sub-array bounded by the iMax and jMax.
#
# Implementing step 0 is actually easier than most people might at
# first suspect. If we define array m so that m[i] is the sum of the
# maximal sub-array that ends at index i, then for all i, m[i] is
# simply the greater of X[i] and m[i - 1] + X[i]. We will illustrate
# below.
#
# i sub-arrays ending at i sum of maximal sub-array
# 0 {-1} m[0]=-1 : {-1}
# 1 {-1,2}; {2} m[1]=2 : {2}
# 2 {-1,2,-3}; {2,-3};{-3} m[2]=-1 : {2,-3}
# 3 {-1,2,-3,12};{2,-3,12};{-3,12};{12} m[3]=12 : {12}
#
# At every step we pick the maximal sub-array from the bunch. If the
# best sub-array ending at X[i-1] has a negative sum, i.e. m[i-1] < 0,
# then there is no benefit to concatenating X[i-1] to X[i] since it
# will pull X[i] down. On the other hand if m[i-1] is positive, the
# best sub-array ending at i will be the one we picked at i-1 added
# to X[i], as in the case where we picked {2,-3} over {-3} at i=2.
#
# The final tally are
#
# for step 0 is m={-1, 2, -1, 12, 35, 31, 26}
# and for step 1 is m={33, 34, 32, 35, 23, -4, -5}.
# Then for step 2, we use
# the results from step 0 to get m[iMax]=35 so that iMax=4;
# then we use the results from step 1 to get m[jMax]=35 so that jMax=3.
# Hence the final answers {A[jMax],...,A[iMax]} = {A[3],A[4]} = {12,23}
#=======================================================================
def subArraySum( A ):
m = [0] * len( A )
startingIndex = len( A ) - 1
endingIndex = 0
# find endingIndex
m[0] = A[0]
for i in range( 1, len( A ) ):
m[i] = max( A[i], m[i - 1] + A[i] )
if ( m[endingIndex] < m[i] ):
endingIndex = i
# find startingIndex
m = [0] * len( A )
m[len( A ) - 1] = A[len( A ) - 1]
for i in range( len( A ) - 2, -1, -1 ):
m[i] = max( A[i], m[i + 1] + A[i] )
if m[startingIndex] < m[i]:
startingIndex = i
if endingIndex <= startingIndex:
return None # no array size less than 2 allowed
result = A[startingIndex:endingIndex + 1]
return result
import unittest
from dynamic_programming import MaxSubarray as algorithm
class Test( unittest.TestCase ):
def testSubArraySum( self ):
lastTwo = [-1, -2, -3, -4, -5, 6, 7]
expResult = [6, 7]
result = algorithm.subArraySum( lastTwo )
self.assertEquals( expResult, result )
firstTwo = [8, 9, -1, -2, -3, -4, -5]
expResult = [8, 9]
result = algorithm.subArraySum( firstTwo )
self.assertEquals( expResult, result )
midTwo = [-1, -2, -3, 12, 23, -4, -5]
expResult = [12, 23]
result = algorithm.subArraySum( midTwo )
self.assertEquals( expResult, result )
midWithNegative = [-1, -2, -3, 12, -4, 23, 0, -4, -5]
expResult = [12, -4, 23]
result = algorithm.subArraySum( midWithNegative )
self.assertEquals( expResult, result )
flat = [0, 0, 0, 0, 0, 0, 0, 0, 0]
expResult = None
result = algorithm.subArraySum( flat )
self.assertEquals( expResult, result )
negatives = [-1, -2, -3, -4, -5]
expResult = None
result = algorithm.subArraySum( negatives )
self.assertEquals( expResult, result )
positives = [1, 2, 3, 4, 5, 6, 7]
result = algorithm.subArraySum( positives )
self.assertEquals( positives, result )
flatBump = [0, 0, 0, 0, 0, 0, 1]
expResult = None
result = algorithm.subArraySum( flatBump )
self.assertEquals( expResult, result )
flatMidBump = [0, 0, 0, 0, 1, 0, 0]
expResult = None
result = algorithm.subArraySum( flatMidBump )
self.assertEquals( expResult, result )
negativeBump = [-1, -1, -1, -1, -1, -1, 1]
expResult = None
result = algorithm.subArraySum( negativeBump )
self.assertEquals( expResult, result )
negativeMidBump = [-1, -1, -1, -1, 1, -1, -1, -1]
expResult = None
result = algorithm.subArraySum( negativeMidBump )
self.assertEquals( expResult, result )