diff --git a/README.md b/README.md new file mode 100644 index 0000000..ca2cc88 --- /dev/null +++ b/README.md @@ -0,0 +1,166 @@ +# Cut Rod Problem — Dynamic Programming + +A complete implementation of the classic **Rod Cutting Problem** from *Introduction to Algorithms* (CLRS), with three algorithmic approaches and an interactive browser-based visualizer. + +--- + +## The Problem + +You have a rod of length **n** inches and a price table that tells you how much each cut length sells for. You want to cut the rod into pieces (or keep it whole) to **maximize total revenue**. + +``` +Length: 1 2 3 4 5 6 7 8 9 10 +Price: 1 5 8 9 10 17 17 20 24 30 +``` + +For a rod of length 4, the best strategy is two pieces of length 2 (5 + 5 = **$10**), not selling it whole for $9. + +This is a classic **optimal substructure** problem: the best way to cut a rod of length n depends on the best ways to cut shorter rods — making it a perfect fit for dynamic programming. + +--- + +## Algorithms + +### 1. Naive Recursion — `simple_cut_rod(p, n)` + +Tries every possible first cut at position i and recurses on the remainder. + +```python +def simple_cut_rod(p, n): + if n == 0: + return 0 + q = float('-inf') + for i in range(1, n + 1): + q = max(q, p[i] + simple_cut_rod(p, n - i)) + return q +``` + +**Time complexity:** O(2ⁿ) — exponential. Each subproblem is recomputed from scratch. + +--- + +### 2. Top-Down DP with Memoization — `memoized_cut_rod(p, n)` + +Same recursion as above, but caches results in an array `r`. If `r[n]` has already been computed, return it immediately. + +```python +def memoized_cut_rod(p, n): + r = [float('-inf')] * (n + 1) + return _memoized_cut_rod_aux(p, n, r) +``` + +**Time complexity:** O(n²) — each of the n subproblems is solved exactly once. + +--- + +### 3. Bottom-Up DP — `bottom_up_cut_rod(p, n)` + +Fills a table `r[0..n]` iteratively from the smallest subproblem up. For each rod length j, tries every first cut i from 1 to j and stores the best result. + +```python +def bottom_up_cut_rod(p, n): + r = [0] * (n + 1) + for j in range(1, n + 1): + q = float('-inf') + for i in range(1, j + 1): + q = max(q, p[i] + r[j - i]) + r[j] = q + return r[n] +``` + +**Time complexity:** O(n²) | **Space complexity:** O(n) + +This is the approach animated in the visualizer. + +--- + +## Complexity Comparison + +| Approach | Time | Space | Notes | +|---|---|---|---| +| Naive recursion | O(2ⁿ) | O(n) call stack | Impractical for n > 30 | +| Top-down (memoized) | O(n²) | O(n) | Natural recursive structure | +| Bottom-up | O(n²) | O(n) | No recursion overhead, cache-friendly | + +--- + +## Project Files + +``` +. +├── cut_rod.py # Python implementation (all 3 algorithms) +├── visualization.html # Interactive browser visualizer +└── Cut Rod Problem/ + └── Cut Rod Problem/ + ├── Program.cs # Original C# implementation + └── Data.txt # Price table (33 prices, space-separated) +``` + +### `Data.txt` — Price Table + +``` +1 5 8 9 10 17 17 20 24 30 33 31 31 31 40 39 20 45 42 46 70 76 77 80 85 90 95 100 110 120 111 200 678 +``` + +Prices for lengths 1 through 33 inches. + +--- + +## Running the Python Version + +```bash +python3 cut_rod.py +``` + +You will be prompted for a rod length. Switch between algorithms by uncommenting the desired line in `main()`: + +```python +best_price = memoized_cut_rod(prices, n) # default +# best_price = bottom_up_cut_rod(prices, n) +# best_price = simple_cut_rod(prices, n) # slow for n > 25 +``` + +**Example output for n = 10:** +``` +Enter number of Inches: 10 +Best Revenue is upto : 30 +Time Elapsed : 0.000021875s +``` + +--- + +## Running the Visualizer + +Open `visualization.html` directly in any modern browser — no server or dependencies needed. + +```bash +open visualization.html # macOS +start visualization.html # Windows +xdg-open visualization.html # Linux +``` + +### Controls + +| Control | Action | +|---|---| +| Rod Length | Set the rod length (1–25). Resets the animation. | +| Speed | Adjust playback speed (1× slow → 10× fast). | +| ▶ Play | Run the animation automatically. | +| ⏸ Pause | Pause at the current step. | +| ⏭ Step | Advance one step forward. | +| ⏮ Back | Go one step backward. | +| ↺ Reset | Restart from the beginning. | + +--- + +## Example: Rod of Length 10 + +The algorithm finds that the maximum revenue is **$30**, achieved by keeping the rod uncut (selling the full 10-inch piece at $30). + +For a rod of length 7, the optimal is **3 + 4 = $17** (8 + 9 = $17, compared to selling whole for $17 — same either way, but the s-table reveals the first cut chosen). + +--- + +## References + +- Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2009). *Introduction to Algorithms* (3rd ed.), Section 15.1 — Rod cutting. diff --git a/visualization.html b/visualization.html index 0fcf792..9a733b9 100644 --- a/visualization.html +++ b/visualization.html @@ -314,6 +314,81 @@ input[type=range] { width: 110px; accent-color: var(--purple); } .r { color: var(--red); } .p { color: var(--purple); } .o { color: var(--orange); } + +/* ── LEGEND ──────────────────────────────────── */ +.legend { + display: flex; + flex-wrap: wrap; + gap: 10px 20px; + justify-content: center; + max-width: 1120px; + margin: 0 auto 20px; + padding: 12px 20px; + background: var(--surf); + border: 1px solid var(--border); + border-radius: 10px; + font-size: .78rem; +} +.legend-item { + display: flex; + align-items: center; + gap: 7px; + color: var(--dim); +} +.swatch { + width: 14px; + height: 14px; + border-radius: 3px; + flex-shrink: 0; +} +.swatch-outline { + width: 14px; + height: 14px; + border-radius: 3px; + flex-shrink: 0; + border: 2px solid; +} + +/* ── EXPLAINER ───────────────────────────────── */ +.explainer { + max-width: 1120px; + margin: 20px auto 0; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 14px; +} +@media (max-width: 800px) { + .explainer { grid-template-columns: 1fr; } +} +.exp-card { + background: var(--surf); + border: 1px solid var(--border); + border-radius: 12px; + padding: 18px 20px; +} +.exp-card h3 { + font-size: .8rem; + text-transform: uppercase; + letter-spacing: .08em; + color: var(--dim); + margin-bottom: 12px; +} +.exp-card p, .exp-card li { + font-size: .83rem; + line-height: 1.65; + color: #a0aab4; +} +.exp-card ul { padding-left: 16px; } +.exp-card li { margin-bottom: 5px; } +.exp-card code { + font-family: 'SFMono-Regular', 'Consolas', monospace; + font-size: .8rem; + background: var(--bg); + padding: 1px 5px; + border-radius: 4px; + color: var(--orange); +} +.exp-card .accent { color: var(--text); font-weight: 600; }
@@ -339,6 +414,34 @@ input[type=range] { width: 110px; accent-color: var(--purple); } + +
+ You have a rod of length n inches and a price table
+ p[1..n] where p[i] is what a piece of length i sells for.
+ You can cut the rod into any combination of integer lengths. The goal is to
+ maximize total revenue.
+
+ A rod of length 4 sold whole earns $9, but two pieces of length 2 earn $5 + $5 = $10. + Finding the best combination by brute force takes O(2ⁿ) time — dynamic programming reduces this to O(n²). +
+i = 1 … j. The candidate revenue is p[i] + r[j−i]: sell the first piece and optimally cut the rest.r[j] holds the maximum over all cuts. s[j] records which cut produced that maximum — used later to reconstruct the actual pieces.s[n] → s[n−s[n]] → … to read off the optimal cut sequence.i); the blue segment is the remainder whose best revenue is already in the table.