Dinesman's Multiple Dwelling Problem

Search for a command to run...

No comments yet. Be the first to comment.
Sometimes an example says more than 1000 pages of documentation. This series will take specific algorithmns and discuss their implementation in PicoLisp step by step. https://rosettacode.org/
"98% of the world population cannot solve this task." (Fake quote attributed to Albert Einstein)
In our previous post, we introduced how to utilize Discrete Event Simulation (DES) in PicoLisp. Now, let's explore another application: a railroad model simulation that includes tracks and trains. The visualization is ASCII-based, so feel free to unl...

The Dining Philosophers

In this post, we will introduce the concept of co-routines and how they are handled in PicoLisp. This will be the foundation for the subsequent posts on Discrete Event Simulation and finally a couple of examples, including an ASCII model railway simu...

One of the many nice things of PicoLisp is that it comes with a built-in database. This makes it easy to collect and store our own data, for example via a mobile app. But not all data is best represented in tables and lists: sometimes a colorful litt...

In the last two posts, we have built a little app that shows the current location and displays the nearest ice cream shops: https://picolisp-explored.com/an-openstreetmap-app-written-in-picolisp-pt-1 https://picolisp-explored.com/an-openstreetmap-ap...

The original task is from the book "Superior Mathematical Puzzles" from Howard P. Dinesman.
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?
This task is kind of similar to the Zebra riddle: We have to translate the statements to our knowledge base so that it can be solved with Pilog.
Let's define a predicate dwelling that returns a list of tenants, where the first list item corresponds to the first floor and the fifth item to the last floor. We know that the list is a permutation of the five names, although we don't know the order. We can use the permute predicate for that:
(be dwelling (@Tenants)
(permute (Baker Cooper Fletcher Miller Smith) @Tenants)
...
topFloor and bottomFloor predicateReading the rules closely, we can identify four rules: "top floor", "bottom floor", "higher floor" and "adjacent floor". Let's implement them in Prolog. The topFloor tenant is the fifth entry in the @Tenants list, while the bottomFloor tenant is the first item:
(be topFloor (@Tenant @Lst)
(equal (@ @ @ @ @Tenant) @Lst) )
(be bottomFloor (@Tenant @Lst)
(equal (@Tenant @ @ @ @) @Lst) )
Now we can add the corresponding rules ot the main dwelling predicate:
(be dwelling (@Tenants)
(permute (Baker Cooper Fletcher Miller Smith) @Tenants)
(not (topFloor Baker @Tenants))
(not (bottomFloor Cooper @Tenants))
(not (or ((topFloor Fletcher @Tenants)) ((bottomFloor Fletcher @Tenants))))
higherFloor predicateNext, we have the following statement:
We define a predicate higherFloor that takes three arguments: two tenants and the full tenant list.
(be higherFloor (@Tenant1 @Tenant2 @Lst)
...
where @Tenant1 is the person living in the higher floor.
Let's assume that we have a sublist @Rest of the tenants list which starts with @Tenant2. In this case, we could split the list into @Tenant2 and the people living above:
(equal (@Tenant2 . @Higher) @Rest)
And we know that @Tenant1 must then be a member of the @Higher sublist:
(equal (@Tenant2 . @Higher) @Rest)
(member @Tenant1 @Higher)
So the only thing we need is a smart way to define @Rest. Luckily, we have the built-in append predicate for this kind of purpose. It takes three arguments: the "first part" of the list, the "second part" of the list, and the full list.
Let's test it to understand what it's doing. Since we're only interested in the "Rest", we can treat the first part as "don't care" using the anonymous variable @:
: (? (append @ @Rest (Baker Cooper Fletcher Miller Smith)))
@Rest=(Baker Cooper Fletcher Miller Smith)
@Rest=(Cooper Fletcher Miller Smith)
@Rest=(Fletcher Miller Smith)
@Rest=(Miller Smith)
@Rest=(Smith)
@Rest=NIL
Given a specific tenant list, append generates for us five sublists that we can use for our higherFloor check.
So our final predicate definition is as follows:
(be higherFloor (@Tenant1 @Tenant2 @Lst)
(append @ @Rest @Lst)
(equal (@Tenant2 . @Higher) @Rest)
(member @Tenant1 @Higher) )
adjacentFloor predicateSimilarly, we can define the adjacentFloor predicate. Two floors are adjacent if they directly follow each other in the list.
Again we can define the @Rest predicate iteratively with append. After that, we simply need to check if the list starts with @Tenant1 and is followed by @Tenant2, or the other way around.
(be adjacentFloor (@Tenant1 @Tenant2 @Lst)
(append @ @Rest @Lst)
(or
((equal (@Tenant1 @Tenant2 . @) @Rest))
((equal (@Tenant2 @Tenant1 . @) @Rest)) ) )
Now the last step is easy: We save everything to a file multipleDwelling.l and execute it with
$ pil multipleDwelling.l +``
: (? (dwelling @Result))
@Result=(Smith Cooper Baker Fletcher Miller)
-> NIL
There is only one solution.
You can download the final script here.
https://illustrain.com/?p=29325
http://rosettacode.org/wiki/Dinesman%27s_multiple-dwelling_problem#PicoLisp