Recursion of JS algorithm

meaning

        A recursive function is a method or function that can call itself directly or indirectly.

// 直接
function do() {
    do();
}

// 间接
function do() {
    do2();
}
function do2() {
    do()
}

        Every recursive function must have a baseline condition (that is, a stopping point, a condition that is no longer called recursively.) Otherwise, it will recurse infinitely. Therefore, there is a famous programming saying: "To understand recursion, you must first understand recursion."

function understandRecursion(doIunderstandRecursion) {
  const recursionAnswer = confirm('Do you understand recursion?'); // function logic
  if (recursionAnswer === true) { // base case or stop point
    return true;
  }
  understandRecursion(recursionAnswer); // recursive call
}

example

        Iterative factorial is a good example of the application of iterative functions.

        n!=n * (n-1) * (n-2) ... * 1

function factorial(n) {
    // 基线条件
    if (n  <= 1) {
        return 1
    }
   return n * factorial(n-1)
}

stack order 

        To understand recursion, you need to understand the call stack sequence of function execution during recursion.

        When we execute factorial(3):

        The execution steps are: factorial(3) => 3 * factorial(2) => 2 * factorial(1) 

At this point, the execution stack         of the function is completed and the call stack begins to pop up.

         factorial(1)  => factorial(2) => factorial(3)

        We can observe it through the browser's developer tools:

         You can see that the location of our breakpoint is when n is 1. There are three factorial functions in the call stack at this time.

      

         Continue going down, at this time n=2. At this point there are two factorial functions left. The factorial function of n=1 has been called back (returning 1).

         callbackfactionrial(1)

Js call stack size limit

        If you forget to add a baseline condition, the recursive function will not execute indefinitely. When the call stack stacks up to a certain limit. The browser will throw an error. This is the so-called stack overflow error.

        This limit is limited by the browser itself. We can test this through functions.

let i = 0;
function recursiveFn() {
  i++;
  recursiveFn();
}

try {
  recursiveFn();
} catch (ex) {
  console.log('i = ' + i + ' error: ' + ex);
}

     Edge exceeded the limit 13903 times

        This value will vary depending on the operating system and browser.

        ES6 has tail call optimization. That is if the last operation within the function is to call the function. It will be controlled through "jump instructions" rather than "subroutine calls". That is, in ES6, recursive functions may not be subject to stack overflow restrictions. Therefore, it is important to have a baseline condition that stops the recursion.

Solving Fibonacci Sequence

        The Fibonacci sequence is a sequence consisting of 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, etc.

         The picture above is an intuitive rule. The abstract rules of computer mathematics are:

  •         The subscript 0 of the sequence (group) corresponds to 0
  •         Sequence subscript 1 corresponds to 1
  •         The sequence subscript n (n>1) corresponds to the sum of the subscript (n-1) value and the subscript (n-2) value. That is value(n) = value(n-1) + value(n-2)

        Through mathematical rules, we can find that as long as we specially deal with the return values ​​of value(0) and value(1). Everything else can be left to the iteration function for accumulation processing.

        In order to reduce the number of iterations, let’s optimize the rules:

  •         The subscript 0 of the sequence (group) corresponds to 0
  •         Sequence subscript 1 corresponds to 1
  •         Sequence subscript 2 corresponds to 1
  •         The sequence subscript n (n>2) corresponds to the sum of the subscript (n-1) value and the subscript (n-2) value. That is value(n) = value(n-1) + value(n-2)
function fibonacci(n) {
    if (n===0) {return 0}
    if (n<=2)  {return 1}
    return fibonacci(n-1) + fibonacci(n-2)
}

         The calling sequence is as shown in the figure. Traverse the past from the left tree to the right tree.

        fibonacci(5) -> fibonacci(4) -> fibonacci(3) -> fibonacci(2) -> fibonacci(1) -> pop the call stack to fibonacci(3) -> fibonacci(2) -> f pop the call stack to fibonacci(4) -> ibonacci(3) -> fibonacci(2) -> fibonacci(1)

Memorize Fibonacci Sequence

        Memoization is an optimization technique that saves the value of the previous result. Similar to caching. For example, in fibonacci(5) above, fibonacci(3) is calculated twice. If the result is stored, it can be calculated one less time.

function fibonacciMemory(n) {
    const memoryResult = [0,1,1];
    const fibonacci = (n) => {
        if (memoryResult[n] != null) return memoryResult[n];
        return memoryResult[n] = fibonacci(n-1) + fibonacci(n-2)
    }
    return fibonacci(n)
}

Use iteration to achieve

export function fibonacciIterative(n) {
  if (n < 1) { return 0; }
  let fibNMinus2 = 0;
  let fibNMinus1 = 1;
  let fibN = n;
  for (let i = 2; i <= n; i++) {
    fibN = fibNMinus1 + fibNMinus2;
    fibNMinus2 = fibNMinus1;
    fibNMinus1 = fibN;
  }
  return fibN;
}

 

Iterative recursion performance comparison

        The iterative version is much faster than the recursive version, so this means that recursion is slower. However, looking at three different versions of the version is easier to understand and usually requires less code. In addition, for some algorithms, the iterative solution may not have tail call optimization, and the unnecessary consumption of recursion may even be eliminated.
        Therefore, we often use recursion because it is easier to solve problems using it

Guess you like

Origin blog.csdn.net/weixin_42274805/article/details/131769230