Jump to content

  • Log In with Google      Sign In   
  • Create Account

Tail Recursion


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
7 replies to this topic

#1 Jethro_T   Members   -  Reputation: 168

Like
0Likes
Like

Posted 21 January 2013 - 12:58 AM

Hey guys, I have a function that I implemented easily using regular recursion but I've been trying to figure out how to write it using tail recursion instead, I can't seem to wrap my head around it though at all.

 

The function is:

 

if n < 4:

f(n) = n

 

else:

f(n) = f(n - 1) + 2(f(n - 2)) + 3(f(n - 3)) + 4(f(n - 4))

 

Can anyone help me figure out how to solve this using tail recursion?

 

 



Sponsor:

#2 Khatharr   Crossbones+   -  Reputation: 3040

Like
0Likes
Like

Posted 21 January 2013 - 02:11 AM

Start out by writing it with standard recursion. We'll go from there.

Are you wanting to explicitly perform tail recursion (with a goto or etc) or are you wanting to trigger the compiler's tail recursion optimization behavior?

Edited by Khatharr, 21 January 2013 - 02:22 AM.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

#3 SiCrane   Moderators   -  Reputation: 9670

Like
1Likes
Like

Posted 21 January 2013 - 07:05 AM

First the obligatory statement that this is a terrible example to use tail recursion with. To transform this into a tail recursive solution you need to manually recreate the stack that the compiler will generate for you. If you wish to preserve the recursive algorithm used here (which is terribly inefficient) then pretend you are the compiler for a moment. To evaluate the recursive calls you would need a temporary accumulator variable, which you would then add in the values of each of the calls. First you would add in 1 * f(n - 1) then 2 * f(n - 2), etc. So there you have two variables for your tail recursive calls: one the accumulator and one of the stack of multiples and function arguments. Each tail recursive call would see if the stack is empty and if so either add to the accumulator or the stack, or otherwise consume a value from the stack and process that.

#4 Álvaro   Crossbones+   -  Reputation: 13912

Like
0Likes
Like

Posted 21 January 2013 - 08:51 AM

This doesn't answer the original question, but this is how I like to think of sequences like this one.

Take the matrix
    0 1 0 0
M = 0 0 1 0
    0 0 0 1
    4 3 2 1

and the vector
    f(n-4)
v = f(n-3)
    f(n-2)
    f(n-1)

When you multiply M*v you get the vector
    f(n-3)
    f(n-2)
    f(n-1)
    f(n)

So the indices are pushed by one in this operation. If you want to advance two steps at once, use M^2.

Now, in order to compute f(n), compute M^n, and multiply the result by the first four terms (0 1 2 3). Computing a power of a matrix can be done in O(log(n)) multiplications. You can also make this computation more efficient by realizing that the matrices involved all live in a 4-dimensional linear subspace, and you can define a special type that contains 4 numbers with the appropriate operations (this part is a bit tricky).

This method is not only computationally efficient, but it gives you a much deeper understanding of the situation. For instance, if you want to find an explicit formula for f(n), you just need to diagonalize M.

Edited by Álvaro, 21 January 2013 - 08:55 AM.


#5 Jethro_T   Members   -  Reputation: 168

Like
0Likes
Like

Posted 21 January 2013 - 05:38 PM

I solved my problem this morning before I got a chance to get on the computer to check the replies to my post.  Seemed very easy this morning when I thought outloud "The value of f(n) is the value of the last 4 values of f(n)."  Naturally this made me realize immediately that I simply needed to start from the bottom 4 cases and build up the value of the function by simply keeping track of the previous 4 values of f(n).

 

My implementation in scheme:

(define (q2iterative n)

  (define (q2iter n a b c d)

    (if (= n 0)

      a

     

      (q2iter (- n 1) (+ a (* b 2) (* c 3) (* d 4)) a b c)

    )

  )

 

  (if (< n 4)

      n

     

      (q2iter (- n 3) 3 2 1 0)

  )

)



#6 Álvaro   Crossbones+   -  Reputation: 13912

Like
1Likes
Like

Posted 21 January 2013 - 07:29 PM

Well, I don't see how that's tail recursion. That's just an iterative solution (which makes much more sense than the recursive one that they insist on teaching in school).

#7 Khatharr   Crossbones+   -  Reputation: 3040

Like
0Likes
Like

Posted 21 January 2013 - 09:54 PM

It makes sense to teach about tail recursion for cases where recursion is appropriate but yeah, this is a bad example.


void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

#8 Yrjö P.   Crossbones+   -  Reputation: 1412

Like
1Likes
Like

Posted 22 January 2013 - 05:22 AM

<blockquote class="ipsBlockquote" data-author="Álvaro" data-cid="5024130"><p>Well, I don't see how that's tail recursion. That's just an iterative solution (which makes much more sense than the recursive one that they insist on teaching in school).</p></blockquote>???<br />q2iter is tail recursive. Calls itself at the end, and only at the end.<br />Of course this is also an iterative solution. Tail recursion is how you do iteration in Scheme.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS