Multiple Dwellings
February 20, 2009
Cooper doesn’t live on the bottom floor; he also doesn’t live on the top floor, because Miller lives above him. Therefore, Cooper must live on floors two through four, as does Fletcher, and since they don’t live on adjacent floors, one of them must live on the second floor and the other on the fourth floor. Assume for the moment that Fletcher lives on the second floor. Then Smith must live on the top floor, since the second and fourth floors are already occupied and he can’t live on the first or third floors adjacent to Fletcher. But then there is no place for Miller to live, since Cooper is on the fourth floor and Miller must be above him. Thus, the assumption that Fletcher lives on the second floor is impossible, so Fletcher lives on the fourth floor and Cooper lives on the second floor. Smith must live on the first floor, since he doesn’t live adjacent to Fletcher on the third or fifth floors, and the second floor is already occupied. Baker must live on the third floor, since he doesn’t live on the top floor. And Miller lives on the top floor, since it is the only place left, and it is above Cooper on the fourth floor.
This problem is easily solved with John McCarthy’s amb
operator, which takes zero or more expressions and non-deterministically returns the value of one of them if it will lead to the success of the overall expression. Amb
is an angelic operator, because it always knows the right answer. It works by backtracking, but the client program never sees the backtracking; from the point of view of the client program, it is as if amb
mysteriously knows the right answer. Here is an implementation of amb
:
(define (fail)
(error 'amb "tree exhausted"))
(define-syntax amb
(syntax-rules ()
((amb) (fail))
((amb expr) expr)
((amb expr ...)
(let ((prev-fail fail))
((call-with-current-continuation
(lambda (success)
(call-with-current-continuation
(lambda (failure)
(set! fail failure)
(success (lambda () expr))))
...
(set! fail prev-fail)
prev-fail)))))))
(define (require condition)
(if (not condition) (amb)))
Given amb
, the puzzle is easy to solve. We arrange five variables, one for each dweller, require that the five variables have distinct values, and require each of the conditions given in the puzzle statement.
(define (distinct? xs)
(cond ((null? xs) #t)
((member (car xs) (cdr xs)) #f)
(else (distinct? (cdr xs)))))
(define (multiple-dwelling)
(let ((baker (amb 1 2 3 4 5))
(cooper (amb 1 2 3 4 5))
(fletcher (amb 1 2 3 4 5))
(miller (amb 1 2 3 4 5))
(smith (amb 1 2 3 4 5)))
(require (distinct? (list baker cooper fletcher miller smith)))
(require (not (= baker 5)))
(require (not (= cooper 1)))
(require (not (= fletcher 5)))
(require (not (= fletcher 1)))
(require (> miller cooper))
(require (not (= (abs (- smith fletcher)) 1)))
(require (not (= (abs (- fletcher cooper)) 1)))
(list (list 'baker baker)
(list 'cooper cooper)
(list 'fletcher fletcher)
(list 'miller miller)
(list 'smith smith))))
The solution can be seen at http://programmingpraxis.codepad.org/8XublsDo.
> (multiple-dwelling)
((baker 3) (cooper 2) (fletcher 4) (miller 5) (smith 1))
Any constraint puzzle can be formulated in this way, using amb
; for instance, amb
provides an easy solution for sudoku puzzles. McCarthy’s original article (from 1961!) describing amb
is at http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.55.8479. Abelson and Sussman give an excellent description at http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-28.html#%_sec_4.3.
In an appartment house?
P!
Haskell (assuming 0 = bottom floor):
import Data.Map (fromList, (!))
import Data.List
data Person = Baker | Cooper | Fletcher | Miller | Smith deriving (Enum, Eq, Ord, Show)
main = print . filter conditions . map (fromList . flip zip [0 :: Int ..]) $ permutations [Baker ..]
conditions xs | xs ! Baker == 4 = False
| xs ! Cooper == 0 = False
| xs ! Fletcher == 0 = False
| xs ! Fletcher == 4 = False
| xs ! Miller < xs ! Cooper = False | abs (xs ! Smith - xs ! Fletcher) == 1 = False | abs (xs ! Cooper - xs ! Fletcher) == 1 = False | otherwise = True [/sourcecode]
[…] Multiple Dwellings Baker, Cooper, Fletcher, Miller and Smith live on different floors of an apartment house that contains only five floors. Baker does not live on the top floor. Cooper does not live on the bottom floor. Fletcher does not live on either the top or the bottom floor. Miller lives on a higher floor than does Cooper. Smith does not live on a floor adjacent to Fletcher’s. Fletcher does not live on a floor adjacent to Cooper’s. Where does everyone live? […]
After some false starts, I came up with Haskell solution that isn’t half bad. Here’s mine:
Clojure code using only a list comprehension for this particular problem.
nb: in my country floors start at zero
https://github.com/ftt/programming-praxis/blob/master/20090220-multiple-dwellings/multiple-dwellings.py
Great problem! I used to do these logic puzzles as a kid by marking off squares on a grid. Here’s my solution in Scala:
Answer:
1.Miller
2.Fletcher
3.Baker
4.Cooper
5.Smith
Here,1. represents Top floor and so on.