For goodness sake if you want to write an example of recursion, walk a tree. That's a great example of a use of recursion given that the iterative version is much clumsier to express.
For goodness sake if you want to write an example of recursion, walk a tree. That's a great example of a use of recursion given that the iterative version is much clumsier to express.
As for walking a tree, I agree, but this optimization does not work well with trees shrug, making lists a nicer example in my opinion.
As a teaching method, I feel that factorial is a relatively good example because it is fairly easy to grok in your head or on paper. Recursing through a tree adds a lot of mental overhead, so I feel like it could be a follow-up example to factorial, but it's probably too hard as an initial example. Also, switching to a stack-safe function using recursion is again much easier for factorial than it would be for traversing a tree.
That said obviously the decision of whether to memoize depends on whether the function is going to be called more than once. If you're not calling fibbonacci more than once there is a closed form solution which is better for many input values than any of these methods.
I don't think that's true; you're pretty much always better off calculating Fibonacci numbers by starting from F_0 and counting up than by using the closed-form solution.
https://towardsdatascience.com/why-is-the-closed-form-of-the...
It covers a couple of points (all of this is me summarizing the article I just linked; I'm not an expert in numeric computing):
- The closed-form expression for Fibonacci numbers involves an irrational number. But we can't do computations on irrational numbers; we're stuck with rational approximations. (Such as IEEE floating point.) Since computing an early Fibonacci number involves raising ϕ to a high exponent, the error involved in giving ϕ a finite representation rapidly compounds, corrupting the result. (According to this article, errors will become visible after F_100. That's quickly enough that you'd be justified in just having a lookup table for the first 100 Fibonacci numbers, which would be much faster than computing them as needed.)
- You can represent the Fibonacci recurrence relation directly in matrix form. This is just the counting-up method written down a different way. Each Fibonacci number is the product of the constant matrix [[1 1] [1 0]] with the vector consisting of the previous two Fibonacci numbers.
- You can exponentiate the matrix n-1 times to get a matrix that converts F_1 and F_0 (known in advance) into F_n and F_{n-1}. This is just as amenable to the shortcuts involved in calculating large exponents of a value as exponentiating ϕ is. But it's exact integer arithmetic.
Right, and repeated squaring can give you the answer to the exponentiation in logarithmic time.
At the risk of stumbling into the No True Memoization fallacy, I wouldn't consider that memoizing -- at least not in the dynamic programming sense. To get O(1), you're not temporarily tabling the results during a single run of the algorithm; rather you're building a semi-permanent database of answers and (effectively, in an amortized sense) discarding the algorithm altogether. I think that's formally known as the "just Google it" approach to optimization. :) From a DP perspective you can get O(N) from memoization on open-form Fibonacci, and no gain on open-form factorial, because the lookup table is ephemeral.
[1] https://www.cs.utexas.edu/users/hunt/research/hash-cons/hash...