Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are reasonable ways to improve solving recursive problems?

I like solving algorithm problems on TopCoder site. I can implement most of the basic recursive problems such as backtracking, dfs... However, whenever I encounter a complex recursion, it often takes me hours and hours. And when I check the solution of other coders, I feel so shame on myself. I've been programming for almost 5 years. I can see the significant improvement on other programming technique such as manipulating string, graphics, GUI ... but not recursion? Can anyone share some experiences how to approach a recursive problems? Thanks!

Update

I'm familiar with Unit-Test methodology. Even before I knew Unit Test, I often write some small test functions to see if the result is what I want. When facing recursive problems, I lost this ability naturally. I can insert several "cout" statements to see the current result, but when the call is nested deeply, I no longer can keep track of it. So most of the time, either I solved it using pencil and paper first or I'm done ( can't use regular method like breaking it into smaller pieces ). I feel like recursion has to work as a whole.

Best regards,
Chan

like image 530
Chan Avatar asked Jan 03 '11 04:01

Chan


People also ask

How do you improve a recursive function?

Bottom-up. Sometimes the best way to improve the efficiency of a recursive algorithm is to not use recursion at all. In the case of generating Fibonacci numbers, an iterative technique called the bottom-up approach can save us both time and space.

Which method is used to solve recursion problems?

A recursive function solves a particular problem by calling a copy of itself and solving smaller subproblems of the original problems. Many more recursive calls can be generated as and when required. It is essential to know that we should provide a certain case in order to terminate this recursion process.

What are the three conditions necessary for controlled recursion?

Like the robots of Asimov, all recursive algorithms must obey three important laws: A recursive algorithm must have a base case. A recursive algorithm must change its state and move toward the base case. A recursive algorithm must call itself, recursively.


2 Answers

I find that a pencil and paper comes in really handy. It's also a good idea to break the problem apart into smaller chunks, such as using a really small data set. The first thing you should do is identify your base condition, the condition that marks the end of the recursive calls. From there you can work on the body of the recursion problem and test/validate it using larger data sets.

I also want to add that speed isn't the only qualifier for being a good engineer. There are many other skills an engineer can possess, including the ability to see and think outside of the box, persuade others as to a particular course of action, break problems down and explain them to the layperson (stakeholders and customers) and much, much more.

like image 99
jmort253 Avatar answered Oct 09 '22 17:10

jmort253


This is a very good question.

The best answer I have is factoring: divide and conquer. This is a bit tricky in C++ because it doesn't support higher order functions well, but you can do it. The most common routines are things like maps and folds. [C++ already has a cofold called std::accumulate].

The other thing you have to consider carefully is how to structure your code to provide tail recursion where possible. One soon gets to recognize tail calls and think of them as loops, and this reduces the brain overload from recursing everywhere quite a bit.

Another good technique is called trust. What this means is, you write a call to a function you may not even have defined yet, and you trust that it will do what you want without further ado. For example you trust it will visit the nodes of a tree bottom up correctly, even if it has to call the function you're currently writing. Write comments stating what the pre- and post-conditions are.

The other way to do this (and I'm sorry about this) is to use a real programming language like Ocaml or Haskell first, then try to translate the nice clean code into C++. This way you can see the structure more easily without getting bogged down with housekeeping details, ugly syntax, lack of localisation, and other stuff. Once you have it right you can translate it to C++ mechanically. (Or you can use Felix to translate it for you)

The reason I said I'm sorry is .. if you do this you won't want to write C++ much anymore, which will make it hard to find a satisfying job. Example, in Ocaml, just add elements of a list (without using a fold):

let rec addup (ls: int list) : int = match ls with 
| [] -> 0                (* empty list *)
| h::t -> h + addup t    (* add head to addup of tail: TRUST addup to work *)

This isn't tail recursive, but this is:

let addup (ls: int list) : int = 
  let rec helper ls sum = match ls with
  | [] -> sum
  | h :: t -> helper t (h+ sum)
  in
helper ls 0

The transformation above is well known. The second routine is actually simpler when you understand what it is doing. I'm too lazy to translate this into C++, perhaps you can transcode it.. (the structure of the algorithms alone should be enough to figure out the syntax)

like image 33
Yttrill Avatar answered Oct 09 '22 16:10

Yttrill