Dynamic Programming Algorithm for Segmented Least Squares
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Segmented least squares fits several straight-line segments to ordered data instead of forcing one line across the whole dataset. The goal is to balance fit quality against model complexity by adding a penalty for each segment.
Dynamic programming is the standard exact solution because the best segmentation ending at point j can be built from the best segmentation ending earlier plus the cost of fitting one final segment. That optimal-substructure property makes the recurrence natural and efficient.
Problem Setup
Assume the points are ordered by x coordinate as (x1, y1) through (xn, yn). For any interval from i to j, define:
- '
error[i][j]as the squared error of the best-fit line through pointsithroughj.' - '
Cas the fixed penalty for introducing one segment.'
Then the objective is to minimize total line-fit error plus C times the number of segments.
The recurrence is:
dp[j] = min over i from 1 to j of dp[i - 1] + error[i][j] + C
with dp[0] = 0.
This says: if the last segment starts at i and ends at j, then the total best cost up to j is the best cost before i plus the cost of fitting that final segment.
A Runnable Python Implementation
The code below computes line-fit error for every interval, fills the dynamic-programming table, and reconstructs the chosen segments.
This implementation is clear rather than heavily optimized. It is good for understanding the recurrence and for moderate input sizes.
Why the Algorithm Works
Every optimal segmentation has a final segment. If that final segment begins at i, then everything before i must also be optimally segmented. Otherwise, replacing the prefix with a cheaper segmentation would improve the whole solution, contradicting optimality.
That is the exact reason dynamic programming applies. Once error[i][j] is known for all intervals, each dp[j] only depends on previously solved subproblems.
The usual time complexity is O(n^3) for a straightforward implementation, because there are O(n^2) intervals and each naive error computation may scan O(n) points. With prefix sums, the line parameters and errors can be precomputed more efficiently, often reducing the practical cost to O(n^2) after preprocessing.
Choosing the Penalty
The penalty term controls the tradeoff between accuracy and overfitting.
- Small penalty means more segments and tighter local fit.
- Large penalty means fewer segments and smoother approximation.
There is no universal best value. In a compression problem you may care about model size, while in a signal-fitting task you may care more about fidelity. Treat the penalty as a modeling decision, not a purely mechanical constant.
Common Pitfalls
A common mistake is applying the algorithm to unsorted points. Segmented least squares assumes an ordered sequence along the x axis. If the points are not sorted, the resulting segments are meaningless.
Another issue is recomputing regression statistics from scratch inside every inner loop without understanding the cost. That is fine for explanation code but not for large datasets.
Developers also sometimes omit the segment penalty. Without it, the trivial best solution is often one segment per point, which defeats the purpose of the model.
Finally, watch out for degenerate intervals where all x values are identical. The slope formula needs special handling in that case.
Summary
- Segmented least squares fits multiple lines while penalizing extra segments.
- Dynamic programming works because the optimal solution has an optimal prefix.
- The key recurrence is
dp[j] = min(dp[i - 1] + error[i][j] + penalty). - Straightforward implementations are easy to understand but can be cubic in time.
- The penalty term controls the balance between smoothness and fit accuracy.
Related reading
- dynamic programming and the use of matrices
- Dynamic programming aspect in Kadane's algorithm
- Dynamic programming Code Wars twice linear algorithm times out
- Dynamic Programming Coin Change Problems
- Dynamic Programming Sum-of-products
- Dynamic quantization in Pytorch starts random training after quantization
- Dynamic Programming Why the need for optimal sub structure
- Dynamically add new queues, bindings and exchanges as beans

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.