This is a problem from my Introduction to Algorithms textbook.
Consider the infinite sequence of numbers An|n>0 = (1,1,3,4,8,11,...) defined recursively as follows.
$$ A_n = \left\{\begin{aligned} &1 && n = 1,2\\ &B_{n-1}+A_{n-2} && n > 2 \end{aligned} \right. \\ B_n = \left\{\begin{aligned} &2 && n = 1,2\\ &A_{n-1}+B_{n-2} && n > 2 \end{aligned} \right. $$
- Write a recursive method to compute the nth value of A, for any given integer n.
- Write a “bottom-up” method to compute the nth value of A, for any given integer n.
I wrote a solution for each but found myself using auxiliary methods in both cases. A recent criticism comes to mind and I wonder if this is unnecessarily complex. Could it be simpler?
Of course any feedback on the efficacy of my approach is welcomed.
My recursive algorithm, stratified into two methods:
private static int computeAuxRecursive(int n) {
if (n <= 0) {
throw new IllegalArgumentException("n mustbe an integer larger than 0");
}
return n <= 2 ? 2 : computeNthRecursive(n - 1) + computeAuxRecursive(n - 2);
}
private static int computeNthRecursive(int n) {
if (n <= 0) {
throw new IllegalArgumentException("n must be an integer larger than 0");
}
return n <= 2 ? 1 : computeAuxRecursive(n - 1) + computeNthRecursive(n - 2);
}
Convenient testing method:
private static String retrieveSequenceRecursive(int length) {
StringBuilder result = new StringBuilder();
for (int i = 1; i <= length; i++) {
result.append(", ").append(computeNthRecursive(i));
}
return result.substring(2);
}
For my bottom-up algorithm I elected to go with a map:
private static Map<Integer, int[]> memoizedMap = new HashMap<Integer, int[]>();
Pre-filled, according to specification, like so:
memoizedMap.put(1, new int[]{1, 2});
memoizedMap.put(2, new int[]{1, 2});
The idea is to use the Nth position requested as a key, and the array of length 2 to hold both the A and B value, using a flag to determine which is being requested.
Actual two methods used for Bottom up algorithm:
private static int computeNthBottomUp(int n) {
if (n <= 0) {
throw new IllegalArgumentException("n must be an integer larger than 0");
}
if (memoizedMap.containsKey(n)) {
return memoizedMap.get(n)[0];
}
memoizedMap.put(n, new int[]{ bottomUpHelper(n, false), bottomUpHelper(n, true) });
return memoizedMap.get(n)[0];
}
// flag boolean to check if we want the B value or not
private static int bottomUpHelper(int n, boolean bFlag) {
if (memoizedMap.containsKey(n)) {
return bFlag ? memoizedMap.get(n)[1] : memoizedMap.get(n)[0];
}
memoizedMap.put(n,
new int[]{
bottomUpHelper(n - 2, false) + bottomUpHelper(n - 1, true),
bottomUpHelper(n - 2, true) + bottomUpHelper(n - 1, false)
}
);
return bFlag ? memoizedMap.get(n)[1] : memoizedMap.get(n)[0];
}
Another convenience method:
private static String retrieveSequenceBottomUp(int begin, int end) {
StringBuilder result = new StringBuilder();
for (int i = begin; i <= end; i++) {
result.append(", ").append(computeNthBottomUp(i));
}
return result.substring(2);
}
Sample tests:
@Test
public void testTenthInSequence() {
assertEquals(76, computeNthRecursive(10));
assertEquals(76, computeNthBottomUp(10));
}
@Test
public void testfirstTenRecursive() {
assertEquals("1, 1, 3, 4, 8, 11, 21, 29, 55, 76", retrieveSequenceRecursive(10));
assertEquals("1, 1, 3, 4, 8, 11, 21, 29, 55, 76", retrieveSequenceBottomUp(1, 10));
}