diff --git a/dynamic-programming/notes.md b/dynamic-programming/notes.md index 33d0f7d3739d7c68b972c6e4212aeb6fa7527d54..dc920f33b6b82cc1bdb0db4c20fd4f480daefdca 100644 --- a/dynamic-programming/notes.md +++ b/dynamic-programming/notes.md @@ -124,17 +124,17 @@ Problem: Wrap a paragraph to a line length, minimizing the sum of the squares of ## Backpointer Class * Information for the final result: - * … + * What was the position of the previous linebreak? * Information to go back: - * … + * What was the position of the previous linebreak? (redundant with the info above) (alternatively, we could track the number of words on the chosen line) * Information to compare quality: - * … + * What was the *total* penalty for the lines placed so far? ## Choosing a Backpointer * Exhaustive search: - * Generate …. - * Check that …. + * Generate start-of-line positions (in reverse order) as long as the character count does not exceed the line length and the start-of-line position does not go negative. + * Check that the backpointer minimizes total penalty. ## Example @@ -146,13 +146,17 @@ Problem: Wrap a paragraph to a line length, minimizing the sum of the squares of Index Previous Index Total Penalty ------ -------------- ------------- 0 ⊥ 0 - 1 … … - 2 … … - 3 … … - 4 … … - 5 … … - 6 … … + 1 0 36 + 2 0 16 + 3 0 4 + 4 0 0 + 5 3 5 + 6 5 14 Reversed Path ---- - 6 ← … + 6 ← 5 ← 3 ← 0 + +> x x x +> x xxxx +> xxxx diff --git a/dynamic-programming/src/features/wrapping/solver.js b/dynamic-programming/src/features/wrapping/solver.js index 062a7fa11ac32231da84d1930b7350c7e2707993..228810490384c2840dac67b5bfa5a067627cc09a 100644 --- a/dynamic-programming/src/features/wrapping/solver.js +++ b/dynamic-programming/src/features/wrapping/solver.js @@ -12,7 +12,17 @@ class Backpointer { function chooseBackpointer(wordLengths, lineLength, backpointers) { const index = backpointers.length; let best = new Backpointer(undefined, Infinity); - // TODO: stub + for (let start = index - 1, characterCount = wordLengths[start]; + start >= 0 && characterCount < lineLength; + --start, characterCount += 1 + wordLengths[start]) { + const candidate = new Backpointer( + start, + backpointers[…].totalPenalty + penalty(…, …), + ); + if (candidate.totalPenalty < best.totalPenalty) { + best = candidate; + } + } console.assert( best.previousIndex !== undefined, `One of the word lengths ${wordLengths} is too long to fit in a line of length ${lineLength}.`,