For complex problems, I suggest doing the problem for small problem sizes and seeing what kinds of patterns you find. For example, in Towers of Hanoi, start with a problem size of one, then two, then three, etc. At some point, you'll probably start to see a pattern, and you'll realize that some of what you are having to do is just like what you had to do on the smaller sized problems, or that it is similar enough that you can use the same technique as before but with some variation.
I just went through the Towers of Hanoi problem myself and studied my own thinking. I started with a problem of size one:
We have one disk on peg A.
*** Move it to peg C.
Done!
Now for two.
We have two disks on peg A.
I need to use peg B to get the first disk out of the way.
*** Move from peg A to peg B
Now I can do the rest
*** Move from peg A to peg C
*** Move from peg B to peg C
Done!
Now for three.
Things start to get a little more interesting. The solution isn't as obvious. However, I've figured out how to move two disks from one peg to another, so if I could move two disks from peg A to peg B, then move one disk from peg A to peg C, and then two disks from peg B to peg C, I'd be done!
My logic for the case of two disks will work, except that the pegs are different. If we put the logic into a function, and make parameters for the pegs, then we can re-use the logic.
def move2(from_peg,to_peg,other_peg):
# We have two disks on from_peg
# We need to use other_peg to get the first disk out of the way
print 'Move from peg '+from_peg+' to peg '+other_peg
# Now I can do the rest
print 'Move from peg '+from_peg+' to peg '+to_peg
print 'Move from peg '+other_peg+' to peg '+to_peg
The logic is then:
move2('A','B','C')
print 'Move from peg A to peg C'
move2('B','C','A')
I can make this simpler by having a move1 function also:
def move1(from_peg,to_peg):
print 'Move from '+from_peg+' to '+to_peg
Now my move2 function can be
def move2(from_peg,to_peg,other_peg):
# We have two disks on from_peg
# We need to use other_peg to get the first disk out of the way
move1(from_peg,other_peg,to_peg)
# Now I can do the rest
move1(from_peg,to_peg)
move1(other_peg,to_peg)
Ok, what about four?
Seems like I can apply the same logic. I need to get three disks from peg A to peg B, then one from A to C, then three from B to C. I've solved moving three already, but with the wrong pegs, so I'll generalize it:
def move3(from_peg,to_peg,other_peg):
move2(from_peg,other_peg,to_peg)
move1(from_peg,to_peg)
move2(other_peg,to_peg,from_peg)
Cool! And wait, move3 and move2 are pretty similar now, and that makes sense. For any sized problem we can move all but one of the disks to peg B, then move one disk from A to C, then move all the disks on peg B to peg C. So our move function can just take the number of disks as a parameter:
def move(n,from_peg,to_peg,other_peg):
move(n-1,from_peg,other_peg,to_peg)
move1(from_peg,to_peg)
move(n-1,other_peg,to_peg,from_peg)
This looks really close, but it doesn't work in the case where n==1 because we end up calling move(0,...). So we need to handle that:
def move(n,from_peg,to_peg,other_peg):
if n==1:
move1(from_peg,to_peg)
else:
move(n-1,from_peg,other_peg,to_peg)
move1(from_peg,to_peg)
move(n-1,other_peg,to_peg,from_peg)
Excellent! What about a problem size of five? We just call move(5,'A','C','B'). Looks like any problem size is the same thing, so our main function is just:
def towers(n):
move(n,'A','C','B')
and we're done!