arrow-left

All pages
gitbookPowered by GitBook
1 of 5

Loading...

Loading...

Loading...

Loading...

Loading...

10.2. Tower of Hanoi

This section discusses recursive and dynamic programming ways of solving the Tower of Hanoi problem.

The followings demonstrate the Tower of Hanoi problem:

hashtag
Abstraction

Let us create the abstract class Hanoiarrow-up-right:

  • L9: returns a list of movements to solve the given problem.

    • n: the total number of rings.

    • source: the ID of the source tower.

    • intermediate

  • L11: returns the string representation of a movement.

hashtag
Recursion

Let us create the class inheriting Hanoi:

hashtag
Dynamic Programming

Let us create the class inheriting Hanoi:

hashtag
Benchmark

The followings show performance comparison:

10.1. Fibonacci Sequence

This section discusses recursive, iterative, and dynamic programming ways of generating the Fibonacci sequence.

The is a sequence of numbers generated as follows:

  • where

public abstract class Hanoi {
    /**
     * @param n            the total number of rings.
     * @param source       the source tower.
     * @param intermediate the intermediate tower.
     * @param destination  the destination tower.
     * @return a list of steps to move all rings in the source tower to the destination tower.
     */
    public abstract List<String> solve(int n, char source, char intermediate, char destination);

    protected String getKey(int n, char source, char destination) {
        return n + ":" + source + "->" + destination;
    }
}
: the ID of the intermediate tower.
  • destination: the ID of the destination tower.

  • HanoiRecursivearrow-up-right
    HanoiDynamicarrow-up-right
    hashtag
    Abstraction

    Let us create the Fibonacciarrow-up-right interface that contains one abstract method:

    • L2: returns the kkk'th Fibonacci number.

    hashtag
    Recursion

    The followings demonstrate how to generate the Fibonacci sequence using recursion:

    Let us create the FibonacciRecursivearrow-up-right class inheriting Fibonacci:

    What is the worst-case complexity of this recursive algorithm?

    hashtag
    Dynamic Programming

    The followings demonstrate how to generate the Fibonacci sequence using dynamic programming:

    Let us create the FibonacciDynamic class inheriting Fibonacci:

    • L7: creates a dynamic table represented by an array where the iii'th dimension is to be filled with the iii'th Fibonacci number.

    • L16: returns the value in the dynamic table if exists.

    • L17: fills in the dynamic table using recursion.

    What is the worst-case complexity of this dynamic programming algorithm?

    hashtag
    Iteration

    For this particular task, the efficiency can be further enhanced using iteration. Let us create the class FibonacciIterativearrow-up-right inheriting Fibonacci:

    hashtag
    Benchmark

    The followings show performance comparisons:

    F0=0,F1=1F_0 = 0, F_1 = 1F0​=0,F1​=1
    Fn=Fn−1+Fn−2F_n = F_{n-1} + F_{n-2}Fn​=Fn−1​+Fn−2​
    n≥2n \geq 2n≥2
    Fibonacci sequencearrow-up-right

    10. Dynamic Programming

    This chapter discusses dynamic programming algorithms in comparison to recursions.

    hashtag
    Contents

    1. Fibonacci Sequence

    hashtag
    References

    public class HanoiRecursive extends Hanoi {
        @Override
        public List<String> solve(int n, char source, char intermediate, char destination) {
            List<String> list = new ArrayList<>();
            solve(list, n, source, intermediate, destination);
            return list;
        }
    
        private void solve(List<String> list, int n, char source, char intermediate, char destination) {
            if (n == 0) return;
            solve(list, n - 1, source, destination, intermediate);
            list.add(getKey(n, source, destination));
            solve(list, n - 1, intermediate, source, destination);
        }
    }
    public class HanoiDynamic extends Hanoi {
        @Override
        public List<String> solve(int n, char source, char intermediate, char destination) {
            List<String> list = new ArrayList<>();
            solve(list, n, source, intermediate, destination, new HashMap<>());
            return list;
        }
    
        private void solve(List<String> list, int n, char source, char intermediate, char destination, Map<String, int[]> map) {
            if (n == 0) return;
            int fromIndex = list.size();
            int[] sub = map.get(getKey(n - 1, source, intermediate));
    
            if (sub != null) addAll(list, sub[0], sub[1]);
            else solve(list, n - 1, source, destination, intermediate, map);
    
            String key = getKey(n, source, destination);
            list.add(key);
            sub = map.get(getKey(n - 1, intermediate, destination));
    
            if (sub != null) addAll(list, sub[0], sub[1]);
            else solve(list, n - 1, intermediate, source, destination, map);
    
            if (!map.containsKey(key))
                map.put(key, new int[]{fromIndex, list.size()});
        }
    
        private void addAll(List<String> list, int fromIndex, int toIndex) {
            for (int i = fromIndex; i < toIndex; i++)
                list.add(list.get(i));
        }
    }
    public interface Fibonacci {
        int get(int k);
    }
    public class FibonacciRecursive implements Fibonacci {
        @Override
        public int get(int k) {
            return switch (k) {
                case 0 -> 0;
                case 1 -> 1;
                default -> get(k - 1) + get(k - 2);
            };
        }
    }
    public class FibonacciDynamic implements Fibonacci {
        @Override
        public int get(int k) {
            return getAux(k, createTable(k));
        }
        
        private int[] createTable(int k) {
            int[] table = new int[k + 1];
            table[0] = 0;
            table[1] = 1;
            Arrays.fill(table, 2, k + 1, -1);
            return table;
        }
    
        private int getAux(int k, int[] table) {
            if (table[k] >= 0) return table[k];
            return table[k] = getAux(k - 1, table) + getAux(k - 2, table);
        }
    }
    public class FibonacciIterative implements Fibonacci {
        @Override
        public int get(int k) {
            if (k < 2) return k;
            int f0 = 0, f1 = 1, f2;
    
            for (int i = 2; i < k; i++) {
                f2 = f0 + f1;
                f0 = f1;
                f1 = f2;
            }
    
            return f0 + f1;
        }
    }
    Tower of Hanoi
    Longest Common Subsequence
    Quiz
    Dynamic Programmingarrow-up-right
    Tower of Hanoiarrow-up-right
    Longest Common Subsequencearrow-up-right

    10.4. Quiz

    This section provides exercises for better understanding in dynamic programming.

    hashtag
    Tower of Hanoi

    Write a report quiz9.pdf that includes answers to the followings.

    • As n increases from 1 to 10, how many times does the auxiliary solve() method get called recursively in and ?

    • Is there clear patterns between n and the number of the method calls made by these classes? Explain the patterns if exist.

    hashtag
    Longest Common Subsequence

    Include answers to the followings in quiz9.pdf:

    • Explain what the values of the dynamic table mean in the class.

    • LCSDynamic pre-populates the dynamic table before making any recursive calls. Is it possible to find a LCS with dynamic programming by populating the dynamic table while making recursive class.

    circle-info

    You may need a different type of a dynamic table to populate it while making recursive calls.

    hashtag
    Extra Credit

    • Create the class under the package.

    • Update the solveAll() that returns all longest common subsequences between two strings.

    HanoiRecursivearrow-up-right
    HanoiDynamicarrow-up-right
    LCSDynamicarrow-up-right
    LCSQuizarrow-up-right
    dynamic.lcsarrow-up-right

    10.3. Longest Common Subsequence

    This section discusses recursive and dynamic programming ways of finding longest common subsequence.

    A subsequence is a sequence that can be derived from another sequence by deleting some or no elements without changing the order of the remaining elements.

    Given a string "ABCDE"

    • Substring: {"A", "BC", "CDE", ...}

    • Subsequence: {all substrings, "AC", "ACE", ...}

    • Not subsequence: {"BA", "DAB", ...}

    Longest common subsequence

    • The longest subsequence commonly shared by multiple strings.

    • e.g., “baal” is a LCS of “bilabial” and “balaclava”.

    Can there be more than one longest common subsequence?

    Application

    • Find the longest common subsequence in DNA (e.g., GAATGTCCTTTCTCTAAGTCCTAAG).

    hashtag
    Abstraction

    Let us create the abstract class :

    hashtag
    Recursion

    Let us create the inheriting LCS:

    • L4: no character is left to compare for either string.

    • L5: when two characters match, move onto c[:i-1] and d[:j-1].

    The followings demonstrate a recursive way of finding a LCS between two strings:

    hashtag
    Dynamic Programming

    Let us create the class inheriting LCS.

    • L4: creates a dynamic table and passes it to the solver.

    • L12: the dynamic table is pre-populated before any recursive calls.

    The following show the dynamic table populated by the previous example:

    Let us define the solve() method:

    • L2: no character is left to compare for either string.

    • L3: when two characters match, move onto c[:i-1] and d[:j-1].

    The followings demonstrate how to find a LCS using the dynamic table:

    L7: gets the longest common subsequence between c[:i-1] and d[:j].
  • L8: gets the longest common subsequence between c[:i] and d[:j-1].

  • L9: returns the longest subsequence.

  • L5: gets the longest common subsequence between c[:i] and d[:j-1].
  • L6: gets the longest common subsequence between c[:i-1] and d[:j].

  • L9: returns the longest subsequence by looking up the values in the dynamic table.

  • LCSarrow-up-right
    LCSRecursivearrow-up-right
    LCSDynamicarrow-up-right
    public abstract class LCS {
        /**
         * @param a the first string.
         * @param b the second string.
         * @return a longest common sequence of the two specific strings.
         */
        public String solve(String a, String b) {
            return solve(a.toCharArray(), b.toCharArray(), a.length() - 1, b.length() - 1);
        }
    
        /**
         * @param c the first array of characters.
         * @param d the second array of characters.
         * @param i the index of the character in {@code c} to be compared.
         * @param j the index of the character in {@code d} to be compared.
         * @return a longest common sequence of the two specific strings.
         */
        protected abstract String solve(char[] c, char[] d, int i, int j);
    }
    public class LCSRecursive extends LCS {
        @Override
        protected String solve(char[] c, char[] d, int i, int j) {
            if (i < 0 || j < 0) return "";
            if (c[i] == d[j]) return solve(c, d, i - 1, j - 1) + c[i];
    
            String c1 = solve(c, d, i - 1, j);
            String d1 = solve(c, d, i, j - 1);
            return (c1.length() > d1.length()) ? c1 : d1;
        }
    }
    public class LCSDynamic extends LCS {
        @Override
        protected String solve(char[] c, char[] d, int i, int j) {
            return solve(c, d, i, j, createTable(c, d));
        }
    
        /**
         * @param c the first string.
         * @param d the second string.
         * @return the dynamic table populated by estimating the # of LCSs in the grid of the two specific strings.
         */
        protected int[][] createTable(char[] c, char[] d) {
            final int N = c.length, M = d.length;
            int[][] table = new int[N][M];
    
            for (int i = 1; i < N; i++)
                for (int j = 1; j < M; j++)
                    table[i][j] = (c[i] == d[j]) ? table[i - 1][j - 1] + 1 : Math.max(table[i - 1][j], table[i][j - 1]);
    
            return table;
        }
    }
    protected String solve(char[] c, char[] d, int i, int j, int[][] table) {
        if (i < 0 || j < 0) return "";
        if (c[i] == d[j])  return solve(c, d, i - 1, j - 1, table) + c[i];
        
        if (i == 0) return solve(c, d, i, j - 1, table);
        if (j == 0) return solve(c, d, i - 1, j, table);
        return (table[i - 1][j] > table[i][j - 1]) ? solve(c, d, i - 1, j, table) : solve(c, d, i, j - 1, table);
    }
    https://www2.slideshare.net/jchoi7s/dynamic-programming-tower-of-hanoi-benchmarkwww2.slideshare.netchevron-right
    https://www2.slideshare.net/jchoi7s/dynamic-programming-fibonacci-sequence-benchmarkwww2.slideshare.netchevron-right