From 9970a1bd9e4506db2e7fd8367b6c78f4dfe39d05 Mon Sep 17 00:00:00 2001 From: Gonzalo Diaz Date: Tue, 6 Aug 2024 20:24:47 -0400 Subject: [PATCH] [Hacker Rank] Interview Preparation Kit: Recursion: Davis' Staircase. Clean code improvements and new generalized solution. --- ...ctci-recursive-staircase-solution-notes.md | 27 ++++++++++++++++ .../ctci_recursive_staircase.test.ts | 27 +++++++++++++++- .../ctci_recursive_staircase.ts | 28 +++++++++------- ...rsive_staircase_generalized.testcases.json | 32 +++++++++++++++++++ 4 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase_generalized.testcases.json diff --git a/docs/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci-recursive-staircase-solution-notes.md b/docs/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci-recursive-staircase-solution-notes.md index 0948b705..9e5d8eb1 100644 --- a/docs/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci-recursive-staircase-solution-notes.md +++ b/docs/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci-recursive-staircase-solution-notes.md @@ -40,3 +40,30 @@ so that repeated cases are not recalculated. The trade-off is that the algorithm now requires more memory to run in less time. + +## Generalized solution + +In order to comply with some clean code best practices, +I noticed that the step limit in the algorithm is a hard-coded number, +so to comply with the "no magic numbers" rule, +I was forced to find a more generalized solution. + +Then I found the following pattern: + +- First cases are: +$ \text{stepPerms(0)} = 0 $ +$ \text{stepPerms(1)} = 1 $ +$ \text{stepPerms(2)} = 2 $ + +- Next step combinations above 2 and less than the step limit are: +$$ \text{stepPerms(number of steps)} = 2^\text{number of steps} + 1 $$ + +- When $ \text{number of steps} $ are above the limit, the pattern is +the sum of latest $ \text{number of steps} $ previous calls +$ \text{stepPerms}(x) $ results as follows: + +$$ \displaystyle\sum_{ + i=\text{number of steps} - \text{limit}} + ^\text{number of steps} + stepPerms(\text{number of steps} - i) +$$ diff --git a/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase.test.ts b/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase.test.ts index db471c24..af535637 100644 --- a/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase.test.ts +++ b/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase.test.ts @@ -1,8 +1,12 @@ import { describe, expect, it } from '@jest/globals'; import { logger as console } from '../../../logger'; -import { stepPerms } from './ctci_recursive_staircase'; +import { + stepPerms, + step_perms_comput_with_cache +} from './ctci_recursive_staircase'; import TEST_CASES from './ctci_recursive_staircase.testcases.json'; +import TEST_CASES_GENERALIZED from './ctci_recursive_staircase_generalized.testcases.json'; describe('ctci_recursive_staircase', () => { it('stepPerms test cases', () => { @@ -18,4 +22,25 @@ describe('ctci_recursive_staircase', () => { }); }); }); + + it('step_perms_comput_with_cache test cases', () => { + expect.assertions(3); + + TEST_CASES_GENERALIZED.forEach((testSet) => { + testSet?.tests.forEach((test) => { + const initial_cache: Record = {}; + const answer = step_perms_comput_with_cache( + test.input, + initial_cache, + test.limit + ); + + console.debug( + `step_perms_comput_with_cache(${test.input}, ${initial_cache}, ${test.limit}) solution found: ${answer}` + ); + + expect(answer).toStrictEqual(test.expected); + }); + }); + }); }); diff --git a/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase.ts b/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase.ts index 3d5100a7..af6f4da6 100644 --- a/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase.ts +++ b/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase.ts @@ -3,36 +3,42 @@ * @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci-recursive-staircase-solution-notes.md]] */ -function step_perms_comput_with_cache( +const TOP_LIMIT = 10 ** 10 + 7; +const STEPS_LIMIT = 3; + +export function step_perms_comput_with_cache( n_steps: number, - cache: Record + cache: Record, + steps_limit: number ): number { if (0 <= n_steps && n_steps <= 2) { return n_steps; } - if (n_steps == 3) { - return 4; - } - const keys = new Set(Object.values(cache)); let result = 0; - for (let i = 1; i < 4; i++) { + for (let i = 1; i <= Math.min(steps_limit, n_steps); i++) { const searchKey = n_steps - i; if (!keys.has(searchKey)) { - cache[n_steps - i] = step_perms_comput_with_cache(searchKey, cache); + cache[searchKey] = step_perms_comput_with_cache( + searchKey, + cache, + steps_limit + ); } result += cache[searchKey]; } - return result; + return result + (n_steps <= steps_limit ? 1 : 0); } export function stepPerms(n: number): number { const initial_cache: Record = {}; - return step_perms_comput_with_cache(n, initial_cache) % (10 ** 10 + 7); + return ( + step_perms_comput_with_cache(n, initial_cache, STEPS_LIMIT) % TOP_LIMIT + ); } -export default { stepPerms }; +export default { stepPerms, step_perms_comput_with_cache }; diff --git a/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase_generalized.testcases.json b/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase_generalized.testcases.json new file mode 100644 index 00000000..2a18c778 --- /dev/null +++ b/src/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci_recursive_staircase_generalized.testcases.json @@ -0,0 +1,32 @@ +[ + { + "title": "Own sample 1", + "tests": [ + { + "input": 4, + "limit": 3, + "expected": 7 + } + ] + }, + { + "title": "Own sample 2", + "tests": [ + { + "input": 5, + "limit": 4, + "expected": 15 + } + ] + }, + { + "title": "Own sample 3", + "tests": [ + { + "input": 6, + "limit": 2, + "expected": 13 + } + ] + } +]