Instead of returning the maximum difference, return both the indices and the maximum difference. Because you pass in sliced lists to the recursive calls, you'll have to adjust the indices returned from recursive rightBest
calls (the leftBest
calls always start with a list running from 'current' 0
to the midpoint).
Use min()
and max()
with a key that alters how the maximum is picked. For picking the index of the maximum value in a a list, generate the indices with range()
, then map those indices back to the list with arr.__getitem__
:
def func(arr):
if len(arr) <= 1:
return 0, 0, 0
mid = len(arr) // 2
left = arr[:mid]
right = arr[mid:]
leftBest = func(left)
ri, rj, rm = func(right)
rightBest = (ri + mid, rj + mid, rm)
key = arr.__getitem__
ci = min(range(0, mid), key=key)
cj = max(range(mid, len(arr)), key=key)
crossBest = (ci, cj, arr[cj] - arr[ci])
return max(leftBest, rightBest, crossBest, key=lambda ijm: ijm[2])
This returns the 2 indices as well as the difference:
>>> func([12, 9, 18, 3])
(1, 2, 9)
You'd have to slice the result to get just the i
, and j
values:
>>> func([12, 9, 18, 3])[:2]
(1, 2)
I'd not even slice the arr
list, because there is really no need to create copies here. Just pass along start
and stop
indices to the recursive functions to replace 0
and len(arr)
. I'd also use a nested function to do the recursive work; the outer function can be used to extract just the (i, j)
pair:
def best_difference_indices(arr):
arr_get = arr.__getitem__
max_get = lambda ijm: ijm[2]
def _func(start, stop):
if stop - start <= 1:
return start, start, 0
mid = (stop - start) // 2 + start
left_best = _func(start, mid)
right_best = _func(mid, stop)
ci = min(range(start, mid), key=arr_get)
cj = max(range(mid, stop), key=arr_get)
cross_best = (ci, cj, arr[cj] - arr[ci])
return max(left_best, right_best, cross_best, key=max_get)
i, j, _ = _func(0, len(arr))
return i, j
Not having to slice makes this faster; with an input of 1000 elements, the second version takes < 3ms to find the 2 indices, while the version with slicing takes ~3.6 ms; a 20% increase.
For 1 million elements, that becomes 3.7 seconds and 4.22 seconds, respectively.