ScholarQuill logoScholarQuillUniversity Notes
My Semester
Explore Notes
Past Papers12 New
Video CoursesNew
Search
Settings
Study Tools
GPA/CGPA Calculator
Schedule & To-Do
Student Portal
Contact
Contact
ScholarQuill logo
My Semester
Explore Notes
Past Papers12 New
Video CoursesNew
Search
Settings
GPA/CGPA Calculator
Schedule & To-Do
Student Portal
Contact
ScholarQuill logoScholarQuillUniversity Notes
Study PortalDC-321Recursion And Recurren…
Ctrl + K
Login
Explore
Search
Semester
Papers
CGPA
    DC-321

    Design & Analysis of Algorithms

    Progress0 / 28 done
    Topics28
    1Introduction2Role of Algorithms in Computing3Analysis on Nature of Input and Size of Input4Asymptotic Notations5Big-O Notation6Big-Ω Notation7Big-Θ Notation8Little-o Notation9Little-ω Notation10Sorting Algorithm Analysis11Loop Invariants12Recursion and Recurrence Relations13Algorithm Design Techniques14Brute Force Approach15Divide-and-Conquer Approach16Merge Sort17Quick Sort18Greedy Approach19Dynamic Programming20Elements of Dynamic Programming21Search Trees22Heaps23Hashing24Graph Algorithms25Shortest Paths26Sparse Graphs27String Matching28Introduction to Complexity Classes
    12 min read
    Topic 12 of 28
    Topic Study Note Illustration
    12

    Recursion and Recurrence Relations

    Recursion and recurrence relations are fundamental concepts in computer science, particularly in algorithm design and analysis. Both are essential for understanding how problems can be broken down...

    Recursion and Recurrence Relations

    Recursion and recurrence relations are fundamental concepts in computer science, particularly in algorithm design and analysis. Both are essential for understanding how problems can be broken down into smaller subproblems and how their solutions relate to each other. Let's explore each of these concepts in detail.


    1. Recursion

    Recursion is a method where a function calls itself in order to solve smaller instances of the same problem. It is a natural way to express problems that can be divided into smaller subproblems, which can be solved independently and then combined to form the solution to the original problem.

    Key Elements of Recursion:

    1. Base Case: The simplest case, where the function stops calling itself and returns a known value. Without a base case, a recursive function would continue calling itself infinitely.
    2. Recursive Case: The part of the function where the function calls itself with smaller or simpler inputs, working towards the base case.

    Example of Recursion: Factorial Function

    The factorial of a number nnn, denoted as n!n!n!, is defined as the product of all integers from 1 to nnn. Recursively, this can be defined as:

    n!=n×(n−1)!with0!=1n! = n \times (n-1)! \quad \text{with} \quad 0! = 1n!=n×(n−1)!with0!=1

    Recursive Implementation:

    def factorial(n):
        if n == 0:        # Base case
            return 1
        else:
            return n * factorial(n - 1)  # Recursive case
    

    In this example:

    • Base case: When n=0n = 0n=0, the function returns 1.
    • Recursive case: The function calls itself with n−1n-1n−1, and the result is multiplied by nnn.

    Benefits of Recursion:

    • Recursion often leads to simpler, more elegant code.
    • It is useful for problems that naturally involve self-similar subproblems, such as tree traversal, searching, and divide-and-conquer algorithms.

    Drawbacks of Recursion:

    • Recursive functions can have higher memory overhead due to the function call stack, especially if not optimized properly.
    • Some problems can be inefficient with naive recursion due to repeated calculations (e.g., computing Fibonacci numbers).

    2. Recurrence Relations

    A recurrence relation is an equation or inequality that describes a function in terms of its value at smaller inputs. In algorithms, recurrence relations are used to express the time complexity of recursive algorithms.

    A recurrence relation for a recursive algorithm expresses how the time complexity of a problem depends on the time complexities of its subproblems.

    General Form of a Recurrence Relation:

    A recurrence relation typically has the following form:

    T(n)=a⋅T(nb)+f(n)T(n) = a \cdot T\left(\frac{n}{b}\right) + f(n)T(n)=a⋅T(bn​)+f(n)

    Where:

    • T(n)T(n)T(n) is the time complexity of a problem of size nnn.
    • aaa is the number of subproblems.
    • nb\frac{n}{b}bn​ is the size of each subproblem (often reduced by a factor of bbb).
    • f(n)f(n)f(n) represents the time spent outside the recursive calls (e.g., combining results, dividing the problem).

    Example of Recurrence Relation: Merge Sort

    The Merge Sort algorithm divides the problem into two halves, recursively sorts each half, and then merges the sorted halves. The recurrence relation for the time complexity T(n)T(n)T(n) of Merge Sort is:

    T(n)=2T(n2)+O(n)T(n) = 2T\left(\frac{n}{2}\right) + O(n)T(n)=2T(2n​)+O(n)

    Where:

    • 2T(n2)2T\left(\frac{n}{2}\right)2T(2n​) represents the time to sort the two halves of the array.
    • O(n)O(n)O(n) represents the time spent in merging the two sorted halves.

    3. Solving Recurrence Relations

    To analyze the time complexity of recursive algorithms, we need to solve the recurrence relations that describe their behavior. There are several methods for solving recurrence relations:

    a. Substitution Method (Induction)

    The substitution method involves guessing the solution to the recurrence and then using mathematical induction to prove it. This method is useful when the recurrence relation is straightforward, but it requires a good guess for the solution.

    Example:

    Solve the recurrence relation for Merge Sort:

    T(n)=2T(n2)+O(n)T(n) = 2T\left(\frac{n}{2}\right) + O(n)T(n)=2T(2n​)+O(n)
    1. Guess: Let's assume T(n)=O(nlog⁡n)T(n) = O(n \log n)T(n)=O(nlogn).
    2. Inductive Step: Assume T(k)≤cklog⁡kT(k) \leq c k \log kT(k)≤cklogk for all k<nk < nk<n. Then for T(n)T(n)T(n):
    T(n)=2T(n2)+O(n)=2⋅c(n2log⁡n2)+O(n)=cnlog⁡n−cn+O(n)T(n) = 2T\left(\frac{n}{2}\right) + O(n) = 2 \cdot c \left(\frac{n}{2} \log \frac{n}{2}\right) + O(n) = c n \log n - c n + O(n)T(n)=2T(2n​)+O(n)=2⋅c(2n​log2n​)+O(n)=cnlogn−cn+O(n)

    Since O(n)O(n)O(n) terms are dominated by nlog⁡nn \log nnlogn, we conclude that T(n)=O(nlog⁡n)T(n) = O(n \log n)T(n)=O(nlogn).

    Thus, Merge Sort has a time complexity of O(nlog⁡n)O(n \log n)O(nlogn).

    b. Master Theorem

    The Master Theorem is a shortcut for solving recurrences of the form:

    T(n)=aT(nb)+O(nd)T(n) = a T\left(\frac{n}{b}\right) + O(n^d)T(n)=aT(bn​)+O(nd)

    Where:

    • aaa is the number of subproblems.
    • bbb is the factor by which the problem size is divided.
    • ddd is the cost of dividing the problem and combining the subproblems.

    The Master Theorem provides a direct way to determine the time complexity by comparing aaa and bdb^dbd.

    The three cases of the Master Theorem are:

    1. Case 1: If a>bda > b^da>bd, the time complexity is O(nlog⁡ba)O(n^{\log_b a})O(nlogb​a).
    2. Case 2: If a=bda = b^da=bd, the time complexity is O(ndlog⁡n)O(n^d \log n)O(ndlogn).
    3. Case 3: If a<bda < b^da<bd, the time complexity is O(nd)O(n^d)O(nd).

    Example: Quick Sort

    The recurrence relation for Quick Sort is:

    T(n)=2T(n2)+O(n)T(n) = 2T\left(\frac{n}{2}\right) + O(n)T(n)=2T(2n​)+O(n)

    This matches the form T(n)=aT(nb)+O(nd)T(n) = a T\left(\frac{n}{b}\right) + O(n^d)T(n)=aT(bn​)+O(nd) with a=2a = 2a=2, b=2b = 2b=2, and d=1d = 1d=1.

    • a=bda = b^da=bd (since 2=212 = 2^12=21), so by Case 2 of the Master Theorem, the time complexity is O(nlog⁡n)O(n \log n)O(nlogn).

    Thus, Quick Sort also has a time complexity of O(nlog⁡n)O(n \log n)O(nlogn) in the average case.

    c. Recursion Tree Method

    The recursion tree method involves drawing a tree where each node represents a subproblem, and the edges represent recursive calls. By summing the costs of all the levels in the tree, we can determine the total time complexity.

    For the recurrence T(n)=2T(n2)+O(n)T(n) = 2T\left(\frac{n}{2}\right) + O(n)T(n)=2T(2n​)+O(n), the recursion tree looks like:

    Level 0: O(n)
    Level 1: 2 * O(n/2) = O(n)
    Level 2: 4 * O(n/4) = O(n)
    ...
    

    Each level has cost O(n)O(n)O(n), and the tree has log⁡n\log nlogn levels, so the total cost is O(nlog⁡n)O(n \log n)O(nlogn).


    4. Common Recurrences

    Here are some common recurrence relations and their solutions:

    • Merge Sort: T(n)=2T(n2)+O(n)T(n) = 2T\left(\frac{n}{2}\right) + O(n)T(n)=2T(2n​)+O(n) → Solution: O(nlog⁡n)O(n \log n)O(nlogn)
    • Quick Sort (average case): T(n)=2T(n2)+O(n)T(n) = 2T\left(\frac{n}{2}\right) + O(n)T(n)=2T(2n​)+O(n) → Solution: O(nlog⁡n)O(n \log n)O(nlogn)
    • Binary Search: T(n)=T(n2)+O(1)T(n) = T\left(\frac{n}{2}\right) + O(1)T(n)=T(2n​)+O(1) → Solution: O(log⁡n)O(\log n)O(logn)
    • Fibonacci Sequence: T(n)=T(n−1)+T(n−2)+O(1)T(n) = T(n-1) + T(n-2) + O(1)T(n)=T(n−1)+T(n−2)+O(1) → Solution: O(2n)O(2^n)O(2n)

    Summary

    • Recursion is a technique where a function calls itself to solve smaller subproblems, and it is crucial for breaking down complex problems.
    • Recurrence relations describe the time complexity of recursive algorithms. They are often solved using methods like substitution, the Master Theorem, or the recursion tree method.
    • Solving recurrence relations helps determine the overall efficiency of recursive algorithms, allowing us to classify them in terms of time complexity.

    Understanding recursion and recurrence relations is essential for analyzing and designing efficient algorithms, especially when using divide-and-conquer or dynamic programming techniques.

    ← Previous topic 11
    Loop Invariants
    Next topic 13 →
    Algorithm Design Techniques

    Past Papers

    Open this section to view and download past papers

    Click on Show Past Papers to load available exam papers.

    Reading Progress
    0%
    12mremaining
    2.1kwords
    Intermediatelevel
    On This Page

    No headings found

    Study Toolkit

    Subject Portal

    Design & Analysis of Algorithms