# SLIDING WINDOW METHOD
# illustrated on a simple sum problem
# =======================================
#
#
# In the given array of positive values
# find the longest contiguous subarray
# which sum is less than 20 (or less than given maxSum).
# For example
# array: [ 5 2 10 10 10 5 6 5 1 10 10]
# longest subarray: [5 6 5 1]
#
#
# The solution method is the sliding window method.
# The window is just a contiguous subarray.
# At the beginning it contains only the first element.
#
# The idea:
# When the sum of items in the window is too small
# expand the right end of the window one position to the right
# so that the sum if items in the window is increased.
# When the sum of items in the window is too big
# shrink the window by moving its left end (=begin)
# one position to the right
# so that the sum of items inside the window is decreased.
# By repeated application of these two conditions
# the window moves through the array until it finally arrives to the
# end of the array. In the process, it examines all feasible subarrays
# and it skips most subarrays which sums are too big or too small.
#
# The advantage of the method is its speed. In each step, either
# the begin or the end index of the subarray is moved to the right,
# so the total running time of the method is proportional
# to twice the length of the array.
# The running time of a brute force approach which would examine
# each possible subarray is proportional to the square or even
# to the cube of the length of the array.
import display
def longestSubArr( arr, maxSum ):
# the window starts as very small, it contains only
# the first element of the array
begin, end = 0, 0, # begin and end of the sliding window
# end refers to the last item in the window
# not behind it
currSum = arr[0] # initial value
# best values were not found yet:
bestBegin, bestEnd, bestSum = -1, -1, -1
# run sliding window
while end < len(arr):
# register best result when it is encountered
if currSum < maxSum and end-begin > bestEnd - bestBegin:
bestBegin, bestEnd, bestSum = begin, end, currSum
# try to expand window to the right
if currSum < maxSum:
end += 1
if end < len(arr): currSum += arr[end]
# else shrink the window from the left
else:
currSum -= arr[begin]
begin += 1
# presentation display
if len(arr) < 30:
display.display1( arr, begin, end )
return bestBegin, bestEnd+1, bestSum
# plain brute force
# Never underestimate the force of brute force!
def longestSubArrBruteForce( arr, maxSum ):
bestBegin, bestEnd, bestSum = -1, -1, -1
bestLen = 0
for iStart in range( 0, len(arr) ):
for iEnd in range( iStart+1, len(arr)+1 ):
currSum = sum( arr[iStart:iEnd] )
if currSum < maxSum:
if bestLen < iEnd-iStart:
bestLen = iEnd-iStart
bestBegin, bestEnd, bestSum = iStart, iEnd, currSum
return bestBegin, bestEnd, bestSum
# OPTIMIZATION?
# A. Improve brute force by considering only segments which are
# longer than the length of the best segment found so far.
# B. Also, when computing new sum of the segment which is only by one
# item longer than the previous segment, just increase the sum,
# do not calculate it anew for the whole segment.
# Note the refined +/- 1 index calculations, making the code prone to errors.
def longestSubArrBruteForceUpd( arr, maxSum ):
bestBegin, bestEnd, bestSum = -1, -1, -1
bestLen = 0
for iStart in range( 0, len(arr) ):
currSum = sum( arr[iStart: iStart+1 + bestLen -1 ] )
for iEnd in range( iStart+1 + bestLen, len(arr)+1 ):
currSum += arr[iEnd-1]
if currSum < maxSum:
bestLen = iEnd-iStart
bestBegin, bestEnd, bestSum = iStart, iEnd, currSum
return bestBegin, bestEnd, bestSum
# // what about pairs by some numpy combinatori etc?
# ------------ MAIN PROGRAM ------------------------------
#
# do experiment with the data and run the program repeatedly
arr = [2, 3, 7, 2, 6, 3, 4, 24, 1, 2, 6, 1, 4, 2, 7, 6]
bestBegin, bestEnd, bestSum = longestSubArr( arr, 20 )
print("Longest subarray: [" + str(bestBegin)+'..'+ str(bestEnd) + "], with sum", bestSum )
# exit()
# Now compare the speed of the methods using bigger data
import time
import random
arrLen = 10000
subArrLen = 40
print(' Array length: ', arrLen )
random.seed( 12312312 )
# random.choices( range_of_possible_values, k = len_of_returned_random_list )
a = random.choices( range(1, 20), k = arrLen )
print()
t1 = time.time()
bestBegin, bestEnd, bestSum = longestSubArr( a, subArrLen )
print("Longest subarray: [" + str(bestBegin)+'..'+ str(bestEnd) + "], with sum", bestSum )
print( a[bestBegin:bestEnd+1] )
t2 = time.time()
print('time: ', t2-t1 )
print()
'''
t1 = time.time()
bestBegin, bestEnd, bestSum = longestSubArrBruteForce( a, 40 )
print("Longest subarray: [" + str(bestBegin)+'..'+ str(bestEnd) + "], with sum", bestSum )
print( a[bestBegin:bestEnd+1] )
t2 = time.time()
print('time: ', t2-t1 )
'''
t1 = time.time()
print()
bestBegin, bestEnd, bestSum = longestSubArrBruteForceUpd( a, 40 )
print("Longest subarray: [" + str(bestBegin)+'..'+ str(bestEnd) + "], with sum", bestSum )
print( a[bestBegin:bestEnd+1] )
t2 = time.time()
print('time: ', t2-t1 )
# print(a)