Scala algorithm: Compute maximum sum of subarray (Kadane's algorithm)
Algorithm goal
Find the maximum sum of a sub-sequence of an array. This problem is known as 'MaxSliceSum' on Codility.
If we have an array like '[1, 2, -3, 4, -5]', the maximum sum of a subarray is 4, that of '[4]'.
Test cases in Scala
assert(
computeForArrayClearKadane(Array[Int](-2, 1, -3, 4, -1, 2, 1, -5, 4)) == 6
)
assert(computeForArrayClearKadane(Array(1, 2, -3, 4, -5)) == 4)
Algorithm in Scala
10 lines of Scala (compatible versions 2.13 & 3.0), showing how concise Scala can be!
Explanation
The mathematics
We need to break the problem down into parts: if we have a sequence \([a,b,c]\), by brute-force, we would need to look through \([a]\), \([b]\), \([c]\), \([a,b]\), \([b,c]\), and \([a,b,c]\). However, if we had computed \([a,b]\), computing \([a,b,c]\) from scratch would be redundant (as we have to do many summations more than once) and here we see a possibility of optimisation.
How do we split it into parts? If we define the result \(M_e\) to be the maximum sum of subarray ending at position \(e\), then \(M_{e+1}\) is either that, plus the value \(V_{e+1}\) at position \(e+1\), or it is the new value \(V_{e+1}\). (this is © from www.scala-algorithms.com)
This leads to a formula \(M_{e+1} = \max\{ M_e, M_e + V_{e+1}\} \), and now that we have the list of maximum subarrays ending at position \(e\), we can find the maximum value from those items.
The code
Once you understand the mathematics, the solution becomes quite straightforward.
Scala concepts & Hints
scanLeft and scanRight
Scala's `scan` functions enable you to do folds like foldLeft and foldRight, while collecting the intermediate results
Stack Safety
Stack safety is present where a function cannot crash due to overflowing the limit of number of recursive calls.
This function will work for n = 5, but will not work for n = 2000 (crash with java.lang.StackOverflowError) - however there is a way to fix it :-)
In Scala Algorithms, we try to write the algorithms in a stack-safe way, where possible, so that when you use the algorithms, they will not crash on large inputs. However, stack-safe implementations are often more complex, and in some cases, overly complex, for the task at hand.
View
The
.view
syntax creates a structure that mirrors another structure, until "forced" by an eager operation like .toList, .foreach, .forall, .count.