1

I'd like to start off by saying this is homework; so I'm not asking for a solution, just some tips. I've been ruminating on this for about a week now. Every solution I come up with doesn't do it recursively, seeing as I can't manage to wrap my head around doing it recursively with the list as the only parameter. My professor said they were able to do it with about 6 helper functions.

As mentioned, I have to solve the problem taking taking the list as the only parameter. Here's an example of what I've got on my latest attempt.

(define (sumSublists lst)
  (if (= (length lst) 0)
      0
      (+ (length (car lst)) (sumSublists (cdr lst)))
  )
)

(define (hanoi towersNum)
  (sub1 (sumSublists towersNum))

    (if (= (sumSublists towersNum) 1)
        (print towersNum)
        (begin (if (= (sumSublists towersNum) 1)
                   (print towersNum)
                   (hanoi '((car towersNum) (cddr towersNum) (cadr towersNum)))
               )
               (if (= (sub1 (sumSublists towersNum)) 1)
                   (print towersNum)
                   (hanoi '((car towersNum) (cadr towersNum) (cddr towersNum)))
               )
               (if (= (sub1 (sumSublists towersNum)) 1)
                   (print towersNum)
                   (hanoi '((cddr towersNum) (cadr towersNum) (car towersNum))) 
               )
        )
    )
)

sumSublists returns how many disks there are in the game. I'm getting an infinite loop since every time it gets used it takes the same value, thus not ever reducing the value. My problem with that, is I'm so used to imperative and OOP with the use of variables that I'm not sure how I can do this when Hanoi only takes one parameter.

The code for Hanoi is my attempt to turn my project from C++ into Scheme.

Any help is appreciated. I'm using Dr. Racket as my IDE by the way.

Darrel Holt
  • 870
  • 1
  • 15
  • 39
  • What are your inputs and expected outputs? The recursive solution to the Towers of Hanoi problem is very simple. In fact, it's only 5 lines of code. However, your problem seems to be a little more complicated. – Aadit M Shah Feb 17 '16 at 06:15
  • For 3 disks we're given (define towers3 '( (1 2 3) () () ) ). For 4 disks we're given (define towers4 '( (1 2 34) () () ) ). The final answer has to be on the second peg. Therefore, it should end up with '( () (1 2 3) () ) and '( () (1 2 3 4) () ) respectively. – Darrel Holt Feb 17 '16 at 07:10
  • I forgot to add, Are you saying there's a possible 5 line answer for this in scheme with the only parameter being the list? because my C++ answer is 5 lines, but it doesn't take the list as it's only parameter. It takes (count, peg1, peg2, peg3). – Darrel Holt Feb 17 '16 at 07:19
  • No, the solution that I was thinking of also takes 4 parameters. It's the same as your C++ solution. – Aadit M Shah Feb 17 '16 at 07:30
  • Do you need to print intermediate values? Because if you don't then you can simple do `(define (hanoi x) (match x [\`(,a ,b ,c) \`(,b ,a ,c)]))`. Actually, don't answer that question. I'm pretty sure you have to print intermediate values. – Aadit M Shah Feb 17 '16 at 07:38

2 Answers2

3

Tower of Hanoi is an inherently recursive problem and it has an elegant recursive solution. I won't give you the solution outright, but I'll explain the intuition behind the solution. Consider:

        +---+                     |                       |
        | 1 |                     |                       |
    +-----------+                 |                       |
    |     2     |                 |                       |
+-------------------+             |                       |
|         3         |             |                       |
+-------------------+   +-------------------+   +-------------------+

          A                       B                       C

Given n discs, the smallest disc is labeled 1 and the largest disc is labeled n. Our job is to move all the discs from stack A to stack C while maintaining the invariant that a bigger disc may never be placed on top of a smaller disc. Without this invariant, the solution would be trivial.

Now, the only way we can move disc n from A to C is by first moving all the discs above it from A to B.

          |                       |                       |
          |                       |                       |
          |                     +---+                     |
          |                     | 1 |                     |
+-------------------+       +-----------+                 |
|         3         |       |     2     |                 |
+-------------------+   +-------------------+   +-------------------+

          A                       B                       C

Our solution is now simple. We can solve our problem in three simple steps. First, we move all the discs above disc n from A to B. Next, we move disc n from A to C. Finally, we move the remaining discs from B to C.

Our problem of moving all the discs from A to C has now been divided into two subproblems:

  1. Move all the discs smaller than disc n from A to B.
  2. Move all the discs smaller than disc n from B to C.

How do we solve these subproblems? We don't have to. By the principle of recursion, we have already solved these subproblems.

Now, we have three stacks labeled A, B and C. However, we want to talk about these stacks in terms of source, destination and auxiliary stacks. When solving the problem of moving disc n from A to C, we call A the source and C the destination. In addition, we need to make use of B which we call the auxiliary stack.

An important thing to notice is that the source, destination and auxiliary stacks keep on changing. For the problem “moving disc n from A to C” the source was A and the destination was C. However, for the problem “moving disc n - 1 from A to B” the source is A but the destination is B. The third stack (not source nor destination) is always the auxiliary stack.

This should give you enough insight to solve the Tower of Hanoi problem. When you do solve it you'll be surprised to find out how simple, elegant and straightforward the solution is compared to imperative code.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • *"Let's forget for a moment that there are `n - 1` discs above disc `n`."*. Let's not. The whole point to recursion is, that we don't. – Will Ness Feb 17 '16 at 11:40
  • @WillNess No, the whole point of recursion is that at any point we only consider two cases: the current case (i.e. `n`) and the previous case (i.e. `n - 1`). Think of lists. When writing the case for `map f (x:xs)` we don't consider that `xs` might not be an empty list. We let recursion take care of it for us. – Aadit M Shah Feb 17 '16 at 14:14
  • *"we don't consider that xs might not be an empty list"* that is my point exactly. we don't go inside the `xs`; similarly, we don't go inside `n-1`. we treat it as a solved case. (I should've said "*structural* recursion", of course). – Will Ness Feb 17 '16 at 15:06
  • @WillNess Okay. I understand what you're saying. I'll update my answer. – Aadit M Shah Feb 17 '16 at 15:08
  • Great, but, the actual question isn't about the recursive algo per se. the OP even says he did it in C++ successfully. The actual problem is about doing it with just one *list* argument. :) – Will Ness Feb 17 '16 at 15:10
  • @WillNess Yes, about that. I was thinking why reinvent the wheel? A function of one argument could simply call another function with more than one argument. – Aadit M Shah Feb 17 '16 at 15:15
  • probably just the HW requirement, to load test the students. – Will Ness Feb 17 '16 at 15:17
  • @WillNess Yes, that's exactly what it's about. It's so simple to do it with more parameters and achieve the optimal efficiency. This method however, is very inefficient and it makes the process much more complicated than it needs to be. – Darrel Holt Feb 19 '16 at 03:56
2

If you know how to solve it with the three arguments holding the three lists A, B and C, you already know how to solve it with the one argument holding the three lists in a list, (A B C).

The problem is counting, as you're not allowed to use a separate argument to hold the number in it. Never mind that, you can count by making a list, its length to serve as the number. And if a list's length is n, it is easy to make a list with length n-1 from it. So in the end all you need is null?.

This scheme would have you always move items from the first entry on the argument list to the second, using the head element as temporary, where you'd open and close new "levels" as needed. The three "poles" would have to be named, to be sorted back into the original order for the final return.

(cf. another answer of mine which discusses the actual recursive algorithm itself).

Community
  • 1
  • 1
Will Ness
  • 70,110
  • 9
  • 98
  • 181