{"id":"code-col-py-memo-dict-not-lru-cache","audience":"college","category":"coding","track":"coding","technique":"constraints","title":"Speed up a recursive function with a hand-written memo","free":false,"task":"Your grid-paths function is far too slow for 16 x 16. Your course hasn't covered functools yet and the autograder blocks imports, so add memoization by hand: a memo=None parameter that becomes a dict inside the function (not a mutable default memo={}), keyed by (r, c). Keep the name, the r and c parameters and the base cases. Reply with exactly 1 code block of at most 12 lines, then exactly two lines: 'Before: O(2^(r+c))' and 'After: O(r*c)', under 25 words outside the code.","materialLabel":"Your function (attached to your prompt automatically)","material":"# paths.py  - practice for CS 2 recursion unit (not graded)\n\ndef grid_paths(r, c):\n    \"\"\"Number of ways to go from (0, 0) to (r, c) moving only right or down.\"\"\"\n    if r == 0 or c == 0:\n        return 1\n    return grid_paths(r - 1, c) + grid_paths(r, c - 1)\n\n\n# timings on my laptop:\n#   grid_paths(10, 10)  -> 184756        0.08 s\n#   grid_paths(13, 13)  -> 10400600      4.9 s\n#   grid_paths(16, 16)  -> (gave up after 5 minutes)\n#\n# Practice autograder message from last time:\n#   \"ImportError blocked: imports are not allowed in this problem\"\n#\n# Things I read but don't understand yet:\n#   - @lru_cache \"just makes it fast\"\n#   - \"never use a list or dict as a default argument\"  (why??)\n#   - there's also a math formula with factorials","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":12},{"type":"maxProseWords","max":25}],"mistakes":[{"title":"Getting @lru_cache","why":"The AI's go-to fix is functools.lru_cache, which your autograder blocks.","fix":"Say: \"No imports; memoize by hand with a dict.\"","checks":["r0"]},{"title":"The mutable default trap","why":"memo={} as a default is shared between calls, a classic bug.","fix":"Add: \"Use memo=None and create the dict inside.\"","checks":["r0"]},{"title":"Switching to the formula","why":"The AI may replace the recursion with factorials, which skips what you're practicing.","fix":"Add: \"Keep the recursion and base cases; key the memo by (r, c).\"","checks":["r1"]},{"title":"A lecture instead of two lines","why":"The AI explains Big-O in paragraphs.","fix":"Show it: \"After the code write exactly: Before: O(2^(r+c)) / After: O(r*c).\"","checks":["r2","maxProseWords"]},{"title":"Several versions","why":"The AI shows a decorator version too.","fix":"Add: \"One version, one code block, at most 12 lines.\"","checks":["codeBlocks","maxCodeLines"]}],"promptHints":[{"label":"Rules out imports","patterns":["no imports","lru_cache","functools","by hand"]},{"label":"Asks for memo=None","patterns":["memo=None","mutable default","none"]},{"label":"Keeps the recursion","patterns":["keep.{0,20}(recursion|base case|name)","same name"]},{"label":"Shows the Big-O lines","patterns":["Before:","After:","big.?o"]},{"label":"Caps the reply","patterns":["only the code","\\d+\\s*lines","no explanation"]}],"hint":"The fastest fix isn't always the one you can use. Tell the AI what's off-limits, the exact memo pattern you want, and the two lines to end with.","rubricLabels":["Memoizes with a dict passed through a memo=None parameter that is set to {} inside the function (no memo={} default), with no imports, lru_cache or cache decorator","Keys the memo by (r, c), keeps the name grid_paths, the r and c parameters, the base case r == 0 or c == 0 returning 1, and passes memo along in both recursive calls","After the code block there are exactly two lines, 'Before: O(2^(r+c))' and 'After: O(r*c)', with no other explanation"]}