3/06: 4. sophisticated solution to maximum subsequence sum recall the 2 loop techniques from the 3/06 lecture: 1. maintain the answer so far 2. maintain something "like" the "answer for the previous value" there is another important loop technique: 0. if you need a quantity (that varies) at some points in the loop, consider maintaining it as part of the loop invariant. the first and second techniques can be understood as special cases of this "zeroth, even-before-the-first", technique _______________________________________________________________________________ 4.0 memorize and use template for processing input with a stopping value! stopping = 0; % stopping value prompt = ['value (' num2str(stopping) ' to stop)? ']; % input prompt num = input(prompt); % current input value % more setup code? ... while num ~= stopping % process $num$: make "progress" num = input(prompt); % read next input value end % finish up code? ... _______________________________________________________________________________ 4.1 first technique to try: answer so far + split the sequence into "processed" and "unprocessed" segments + maintain the answer so far (for the processed segment) + justification: if we can extend the processed segment to include the entire sequence, then answer so far = answer for processed segment = answer for entire sequence = final, correct answer invariant (picture): | +-----------|-----+-------------+ s | processed | num | unprocessed | 0 +-----------|-----+-------------+ \_ _/ | V sofar = max subsequence sum of input values strictly before $num$ ("strictly before" $num$ = "up to but excluding" $num$) invariant (words): $num$ = current input value $sofar$ = max subsequence sum of input values strictly before $num$ our code (processing input template + invariant) thus now looks like this: stopping = 0; % stopping value prompt = ['value (' num2str(stopping) ' to stop)? ']; % input prompt num = input(prompt); % current input value sofar = 0; % max subsequence sum of input values strictly before $num$ % more setup code? ... % inv: maintaing variables above while num ~= stopping % process $num$: update $sofar$ ... num = input(prompt); % read next input value end % finish up code? ... _______________________________________________________________________________ 4.2 introspection (how *we* do it) how do we process $num$ to update $sofar$ to make progress? it is not immediately obvious how to get from | +-----------|-----+-------------+ s | processed | num | unprocessed | 0 +-----------|-----+-------------+ \_ _/ | V sofar = max subsequence sum of input values strictly before $num$ to | +-----------+-----+-------------+ s | processed | num | unprocessed | 0 +-----------+-----+-------------+ \_ _/ | V sofar = max subsequence sum of input values strictly before $num$ therefore, use introspection + try solving the problem by hand on some small examples + pay attention to the steps *we* use + pay attention to what *does* changes and what does *not* change when we include another element to the processed segment for a "small" example, let's use $s$ from lec-03-06.1.txt, and see how to recompute $sofar$ from scratch when we extend the processed segment: +---+----+----+----+---+ s | 6 | -2 | -1 | -2 | 7 | 0 CHANGES: +---+----+----+----+---+ : : : : : : : : : : : : 0 [] : : : : : : .....: : : : : : to include $6$ in processed segment : : : : : + look at all previous subsequences AND .....[ 6 ] : : : : + all subsequences ending on $6$ : : : : [ 6 + -2 ] : : : to include (first) $-2$ in processed segment .........[ -2 ] : : : + look at all previous subsequences AND : : : + all subsequences ending on (first) $-2$ [ 6 + -2 + -1 ] : : [ -2 + -1 ] : : to include $-1$ in processed segment ..............[ -1 ] : : + look at all previous subsequences AND : : + all subsequences ending on $-1$ [ 6 + -2 + -1 + -2 ] : [ -2 + -1 + -2 ] : to include (second) $-2$ in processed segment [ -1 + -2 ] : + look at all previous subsequences AND ...................[ -2 ] : + all subsequences ending on (second) $-2$ : [ 6 + -2 + -1 + -2 + 7 ] to include $7$ in processed segment [ -2 + -1 + -2 + 7 ] + look at all previous subsequences AND [ -1 + -2 + 7 ] + all subsequences ending on $7$ [ -2 + 7 ] ........................[ 7 ] thus, we see that + "make progress" = "process $num$" = "make new answer $sofar$ take $num$ into consideration" + translates into: the new answer $sofar$ = max of sums of subsequences strictly before $num$ and of sums of subsequences ending on $num$ (by our observations from introspection) = max (max of sums of subsequences strictly before $num$, max of sums of subsequences ending on $num$) (by some math: max([Y Z]) = max(max(Y), max(Z)) in words: "max of Ys and Zs = max of (max of Ys, max of Zs)") = max (old answer $sofar$, max of sums of subsequences ending on $num$) (by definition of $sofar$ in the loop invariant) + so to complete the code, we need an efficient way to compute "max of sums of subsequences ending on the current ``old'' value of $num$", where "current ``old'' value of $num$" was added to remind ourselves that at the end of the loop body we will read a new value for $num$, and above we want the value previous to that new value. + it is now time to use our new, zeroth loop technique _______________________________________________________________________________ 4.3 zeroth --> second technique to try: maintain info. about previous value + introspection told us we need the value (let's call it $(*)$): (*) = "max of sums of subsequences ending on the ``old'' value of $num$, before the new input value is assigned to $num$" + the zeroth loop technique tell us to try adding a variable that maintains the value (*) as part of the loop invariant + let us rewrite the value (*) to be part of the loop invariant, which must hold at the very end of the loop body, after the new input value of $num$ has been read: (*) = "max of sums of subsequences ending on the previous input value" + look! this has turned out to be an application of our 2nd technique! _______________________________________________________________________________ 4.4 code for sophisticated solution picture of new invariant: endprev = max sum of subsequences ending on previous input value __/\__ / \| +--------------+-|-----+-------------+ s | processed | | num | unprocessed | 0 +--------------+-|-----+-------------+ \__ __/ | V sofar = max subsequence sum of input values strictly before $num$ code: stopping = 0; % input stopping value prompt = ['number (' num2str(stopping) ' to stop)? ']; % user input prompt num = input(prompt); % current input value sofar = 0; % max subsequence sum of input values strictly before $num$ endprev = 0; % max sum of subsequences ending on previous input value % inv: maintain variable definitions above while num ~= stopping % process $num$: update $sofar$ and $endprev$ endprev = num + max(0, endprev); sofar = max(sofar, endprev); num = input(prompt); % read next input value end + concise + fast: $O(n)$ (bounded steps in loop body, executed $n$ times) + little memory: $O(1)$ (bounded vector $prompt$ + 4 scalars)