As you probably figured out, the reason your recursive solution is slow is because you recompute answers that had already been computed before.
This is why memoization works, and it allows you to maintain your top down algorithmic translation of the sequence as mathematically defined.
The disadvantage of complete memoization is that the table can be arbitrarily large. If O(1) time (at the cost of O(n) space) is not required, and O(n) time is sufficient, it is possible to perform the computation with O(1) space, and this has been illustrated in other answers as well.
All that is really required is that for any recursive calculation of test(n-1)
, the values for test(n-2)
and test(n-3)
should also be available. If you do not like creating a helper recursive function to track this information, then the below is an alternative that uses a single recursive function.
int test(int n) {
typedef struct { int x[2]; } state;
static const state init = { 0, 1 };
static state last;
if (n > 0) {
last = init;
return test(-n);
}
n = -n;
if (n < 3) return n;
// After the below call, last has test(n-2) and test(n-3)
int x = test(-(n-1));
int result = x + last.x[0] + last.x[1];
last.x[0] = last.x[1];
last.x[1] = x;
return result;
}
Try it online!