Courses/CS 460/Fall 2005/Homework
From CSWiki
References | Homework | Questions | Course notes | Oz notes
Contents |
[edit] Due Oct 1
- Read the tutorial through SMerge as we did in class.
- Create 4 or 5 programs on binary trees and lists to illustrate and exercise the Oz features discussed.
- Familiarize yourself with Sudoku.
- Create a homework solutions page for yourself in this namespace. (Recall that to create a page called PageX in this namespace, refer to it as [[{{NAMESPACE}}:PageX | visible text]].
- Note that you type the literal characters {{NAMESPACE}}: as part of the link.
- Organize your homework page the same way this page is organized, i.e., with a separate section for each week's homework, most recent first.
- Note that pages are timestamped when they are created or modified. If one person copies from another, it will be easy to see whose page was created first.
[edit] Homework submissions
The links below don't work because the namespace of this page is the default (null) namespace. To get to the old Homework page with links that work, go to Homeworks
[edit] Due Oct 8
Consider the following non-recursive version of merge-sort. For concreteness, we'll use the list [e f c b d a] as an example.
- Convert the original list into a list of singleton lists [[e] [f] [c] [b] [d] [a]].
- Merge pairs of lists to get [[e f] [b c] [a d]]
- Repeat the pair merge process until only one sublist is left:
- [[b c e f] [a d]]
- [[a b c d e f]]
- The one remaining sublist [a b c d e f] is the answer.
Write your version of this.
Note: You will have to use tail recursion to implement the iteration. But this will not be the standard recursive merge sort because you don't start with a complete list, which you split in half, sort each half recursively, and then merge together.
Implement 5 other recursive methods from the List module. Select at least two for which an argument is a function or a procedure.
Please don't use any of the built-in loop constructs. You may define your own For or other loop procedures and then use them.
[edit] Homework submissions
Before entering a pointer to your homework, please read about Subpages.
[edit] Due Oct 15
Write 5 list procedures or functions (like last week) but use only choice. Don't use case or if-then-else.
Here is a version of Delete/3. It is like IsMember/2 except that if the first parameter can be unified with an element of the second, it returns in its 3rd parameter position the list that's left after the first parameter is deleted from the second. If the first parameter can't be unified with an element of the second, Delete/3 fails.
proc {Delete X Xs XRest}
Head Tail in
Xs = Head | Tail
choice
Head = X
XRest = Tail
[] NewTail in
XRest = Head | NewTail
{Delete X Tail NewTail}
end
end
You can think of this also as a select procedure. It selects an element from Xs, which it assigns to X. The XRest become the remaining elements of Xs.
Include Permute/2 as one of the five procedures that you write.
The following line should generate all permutations of [a b c d].
{Browse {SearchAll proc {$ Perm} {Permute [a b c d] Perm} end}}
Hint: Permute is not long. One version of it has the following form.
proc {Permute In Out}
% variable declarations
in
choice
% choice 1 is two statements.
% It deals with the case when In is nil.
[] % choice 2 is three statements
% 1. Select an element from In.
% 2. Permute the remaining elements.
% 3. Put the selected element at the front of the result.
end
end
[edit] Homework submissions
[edit] Due Oct 22
Select and solve 3 puzzles from the logic puzzle web sites in the References box on the main course page.
Don't do the five sisters puzzle, the one we started in class. Its solution follows.
[edit] Five Sisters Problem
Five sisters all have their birthday in a different month (one of february, march, june, july, or december) and each on a different day of the week (one of sunday, monday, wednesday, friday, or saturday). Using the clues below, determine the month and day of the week each sister's birthday falls.
- Paula was born in March but not on Saturday. Abigail's birthday was not on Friday or Wednesday.
- The girl whose birthday is on Monday was born earlier in the year than Brenda and Mary.
- Tara wasn't born in February and her birthday was on the weekend.
- Mary was not born in December nor was her birthday on a weekday. The girl whose birthday was in June was born on Sunday.
- Tara was born before Brenda, whose birthday wasn't on Friday. Mary wasn't born in July.
[edit] Five Sisters Solution
This looks like a lot of code, but most if it expresses the clues.
local
% These variables may be defined outside the problem since
% they are read, not written. Of course, they may also
% be defined inside the problem as in the Zebra puzzle.
% In that case, they must be passed as arguments to
% IsADay, IsAMonth, and IsASister unless those are
% also defined inside the problem.
Days = [sunday monday wednesday friday saturday]
Months = [february march june july december]
Sisters = [abigail brenda mary paula tara]
% If Zs is not instantiated (or at least not bounded), then
% Xs and Ys should be. Otherwise the number of solutions will grow
% without limit.
proc {Append ?Xs ?Ys ?Zs}
choice
Xs = nil
Ys = Zs
[] Head XRest ZRest in
Xs = Head|XRest
Zs = Head|ZRest
{Append XRest Ys ZRest}
end
end
% Month1 precedes Month2 in Months.
% Instantiates the months if initially
% uninstantiated.
proc {EarlierInYear ?Month1 ?Month2}
Rest in
{IsATail Months Month1|Rest}
{IsAMember Month2 Rest}
end
% Value is the value of field Property in the record SPs
% Both Property and Value may be uninstantiated
proc {IsAPropertyValue SPs ?Property ?Value}
{IsAMember Property#Value {Record.toListInd SPs}}
end
% Day is a day.
proc {IsADay ?Day} {IsAMember Day Days} end
% Month is a Month
proc {IsAMonth ?Month} {IsAMember Month Months} end
% Sis is a Sister.
proc {IsASister ?Sis} {IsAMember Sis Sisters} end
% Look at this neat definition for IsAMember/2.
% X is a member of Xs.
proc {IsAMember ?X Xs} {Append _ X|_ Xs} end
% Tail is a tail of the list Xs
proc {IsATail ?Xs ?Tail} {Append _ Tail Xs} end
proc {Clue1 SPs}
Day1 Day2 in
% Paula was born in March
{IsAPropertyValue SPs march paula}
% but not on saturday
{IsAPropertyValue SPs Day1 paula}
% Ensure that Day1 is a day (rather than
% a month). Otherwise Day1 may be
% instantiated to march.
{IsADay Day1}
Day1 == saturday = false
% Abigail's birthday was not friday or wednesday
{IsAPropertyValue SPs Day2 abigail}
{IsADay Day2}
Day2 == friday = false
Day2 == wednesday = false
end
proc {Clue2 SPs}
Month1 Month2 Month3 Sister in
% The girl whose birthday is on Monday
{IsAPropertyValue SPs monday Sister}
{IsASister Sister}
% (by implication, not brenda or mary)
Sister == brenda = false
Sister == mary = false
% was born earlier in the year (on Month1)
{IsAPropertyValue SPs Month1 Sister}
% than brenda
{IsAPropertyValue SPs Month2 brenda}
% Don't need {IsAMonth Month_i} since
% {EarlierInYear _ _} instantiates its arguments.
{EarlierInYear Month1 Month2}
% and mary.
{IsAPropertyValue SPs Month3 mary}
{EarlierInYear Month1 Month3}
end
proc {Clue3 SPs}
Month in
% Tara wasn't born in February
{IsAPropertyValue SPs Month tara}
{IsAMonth Month}
Month == february = false
% and her birthday was on the weekend.
choice SPs.saturday = tara [] SPs.sunday = tara end
end
proc {Clue4 SPs}
Month in
% Mary was not born in December
{IsAPropertyValue SPs Month mary}
{IsAMonth Month}
december == Month = false
% nor was her birthday on a weekday
choice SPs.saturday = mary [] SPs.sunday = mary end
% The girl whose birthday was in June was born on Sunday
SPs.june = SPs.sunday
end
proc {Clue5 SPs}
Month1 Month2 Day in
% Tara was born before brenda. I'm presuming this means
% in an earlier month.
{IsAPropertyValue SPs Month1 tara}
{IsAPropertyValue SPs Month2 brenda}
{EarlierInYear Month1 Month2}
% whose (presumably brenda's) birthday wasn't on Friday.
{IsAPropertyValue SPs Day brenda}
{IsADay Day}
Day == friday = false
% Mary wasn't born in july.
% Clue 2 already assigned a month for Mary.
% All we have to do here is make sure it isn't July.
SPs.july == mary = false
end
fun {FiveSisters}
Properties = {FoldR [Months Days] Append nil}
SPs = {MakeRecord sisterProperties Properties}
in
% To debug, comment out all the clues except the one
% you are working on and the ones that already work.
{Clue1 SPs}
{Clue2 SPs}
{Clue3 SPs}
{Clue4 SPs}
{Clue5 SPs}
% The clues don't explicitly tell us
% in which month abigail was born. So
% require that she be born in some month.
local Month in
{IsAPropertyValue SPs Month abigail}
{IsAMonth Month}
end
SPs
end
in
{Browse {SearchAll FiveSisters}}
end
[edit] Homework submissions
[edit] Due Oct 29
Select and solve 3 puzzles from the logic puzzle web sites in the References box on the main course page. The solution to the five sisters puzzle using the second representation is posted below.
The thread bomb technique works. See Thread bombs for a short example.
[edit] The five sisters using an alternate representation (and using thread bombs)
We build a record with a field for each sister. Each field has as its value a record with a field for day and a field for a month. The answer looks like this.
[sisters(abigail:properties(day:monday month:february)
brenda:properties(day:wednesday month:december)
mary:properties(day:sunday month:june)
paula:properties(day:friday month:march)
tara:properties(day:saturday month:july))]
First we list some general utilities. Then we express the problem in terms of these utilities. Note how easy it is to write the clues. They are pretty much direct translations from the Enlgish. At the end of the puzzle we have to add a few lines that require that all values are instantiated to something.
[edit] Utilities
declare
% Every member of Xs has a value from Values.
% Instantiates those that don't.
proc {AllInstantiated Xs Values}
{ForAll Xs proc {$ X} {IsIn X Values} end}
end
proc {Append ?Xs ?Ys ?Zs}
choice
Xs = nil
Ys = Zs
[] X Xr in
Xs = X | Xr
Zs = X | {Append Xr Ys}
end
end
% Creates a list of the values for Field in the elements
% of Elts. Assumes Elts is a homgeneous list whose
% members are records with a field named Field.
fun {GetFields Field Elts}
{Map Elts fun {$ Elt} Elt.Field end}
end
% Returns the first index of X in Xs. X and Xs must be
% instantiated or will suspend until they are.
% Fails if X is not in Xs.
fun {IndexOf X Xs}
fun {IndexOf3 X Xs N}
case Xs of
Y | Rest then
if X == Y then N else {IndexOf3 X Rest N+1} end
end
end
in
{IndexOf3 X Xs 1}
end
proc {IsIn ?X Xs} {Append _ X|_ Xs} end
proc {IsAnElt ?X Elts} {IsIn X {Record.toList Elts}} end
%% Constraints
% Generate thread bombs that ensure that all the members
% of List are distinct.
proc {AllDistinct Xs}
case Xs of X|Xr then
for Y in Xr do
thread X==Y = false end
end
{AllDistinct Xr}
else skip end
end
% Plant a thread bomb that goes off if X and Y become
% instantiated to the same value.
proc {NotEqual X Y} thread X == Y = false end end
% A thread bomb that ensures that X1 precedes X2 in Xs.
proc {Precedes ?X1 ?X2 Xs}
thread {IndexOf X1 Xs} < {IndexOf X2 Xs} = true end
end
%% Propagator utilities
% If the fields common to R1 and R2 are all instantiated, returns
% true/false depending on whether they are all equal (==).
% Suspends until fields become instantiated.
fun {Match R1 R2}
{Record.all
{Record.zip R1 R2 fun {$ A B} A == B end}
fun {$ X} X == true end}
end
% If {Match Condition R} then {UnifyRecs Result R}.
% Does this in a thread to avoid blocking the main thread.
% Also acts as a constraint in case the unification fails.
proc {Propagate Condition Result R}
thread if {Match Condition R} then {UnifyRecs Result R} end end
end
% Propagates Condition -> Result to all the fields in the record Rec.
% Each field is done in its own thread. See Propagate.
proc {PropagateAll Condition Result Rec}
{Record.forAll Rec proc {$ R} {Propagate Condition Result R} end}
end
% Unifies the values of the fields common to R1 and R2
proc {UnifyRecs R1 R2}
{Record.zip R1 R2 fun {$ A B} A = B end _}
end
[edit] Problem specific information
local Days = [sunday monday wednesday friday saturday] Months = [february march june july december]
proc {Clue1 Sisters}
% Paula was born in March
Sisters.paula.month = march
% but not on saturday
{NotEqual Sisters.paula.day saturday}
% Abigail's birthday was not friday or wednesday
{NotEqual Sisters.abigail.day wednesday}
{NotEqual Sisters.abigail.day friday}
end
proc {Clue2 Sisters}
Month
in
% The girl whose birthday is on Monday
{IsAnElt properties(day:monday month:Month) Sisters}
% was born earlier in the year than Brenda
{Precedes Month Sisters.brenda.month Months}
% and Mary
{Precedes Month Sisters.mary.month Months}
end
proc {Clue3 Sisters}
% Tara wasn't born in February
{NotEqual Sisters.tara.month february}
% and her birthday was on the weekend.
choice Sisters.tara.day = saturday [] Sisters.tara.day = sunday end
end
proc {Clue4 Sisters}
% Mary was not born in December
{NotEqual Sisters.mary.month december}
% nor was her birthday on a weekday
{NotEqual Sisters.mary.day monday}
{NotEqual Sisters.mary.day wednesday}
{NotEqual Sisters.mary.day friday}
% The girl whose birthday was in June was born on Sunday.
{PropagateAll properties(month:june) properties(day:sunday) Sisters}
% Formerly, not using Propagate. Puts these properties
% into as many records as possible, generating a
% separate computation for each.
% {IsAnElt properties(day:sunday month:june) Sisters}
end
proc {Clue5 Sisters}
% Tara was born before brenda,
{Precedes Sisters.tara.month Sisters.brenda.month Months}
% whose birthday wasn't on Friday.
{NotEqual Sisters.brenda.day friday}
% Mary wasn't born in July.
{NotEqual Sisters.mary.month july}
end
fun {FiveSisters}
Sisters = {MakeRecord sisters [abigail brenda mary paula tara]}
Ms
Ds
SisterList = {Record.toList Sisters}
in
% Create property records for each sister.
{Record.forAll Sisters proc {$ S} S = {Record.make properties [day month]} end}
% Get the list of Month and Day variables.
% Constrain them to be distinct.
Ms = {GetFields month SisterList }
{AllDistinct Ms}
Ds = {GetFields day SisterList }
{AllDistinct Ds}
% Run the clues
{Clue1 Sisters}
{Clue2 Sisters}
{Clue3 Sisters}
{Clue4 Sisters}
{Clue5 Sisters}
% Ensure that every day and month has a value.
{AllInstantiated Ms Months}
{AllInstantiated Ds Days}
% Return Sisters as the answer
Sisters
end
in
{Browse {SearchAll FiveSisters}}
end
[edit] Homework submissions
[edit] Due Nov 5
[edit] Individual Meetings.
Don't forget that this week is devoted to individual meetings.
[edit] LET + THERE + BE = LIGHT
Solve this the same way we solved SEND + MORE = MONEY
[edit] Rotating digits
Find digit values for A, B, C, D, E, and F such that the numbers made by concatenating those digits have the follow property.
ABCDEF * 1 = ABCDEF
ABCDEF * 3 = BCDEFA
ABCDEF * 2 = CDEFAB
ABCDEF * 6 = DEFABC
ABCDEF * 4 = EFABCD
ABCDEF * 5 = FABCDE
Hint: Define a proc {Mult L1 X L2} where L1 and L2 are each lists of exactly 6 digits.
The proc establishes a constraint propagator for
L1 * X =: L2,
when L1 and L2 are interpreted as the digits in two numbers.
Use the same techniques as in SEND + MORE = MONEY.
Then the main program will have a number of lines including, for example, one that reads:
{Mult [A B C D E F] 2 [C D E F A B]}
[edit] Divisible digits
Find all permutations of the digits 0 .. 9 that as a number are each divisible by each digit (other than 0). One answer is 1234759680. Because I don't know how many others there are, you might first try your program using SearchOne instead of SearchAll. Once it works for SearchOne, try SearchAll. Here is a Google search for 1234759680. The first two links are http://users.erols.com/jyavins/1234759680.htm and http://rec-puzzles.org/new/sol.pl/arithmetic/digits/divisible, which is where I found the problem.
[edit] Homework submissions
[edit] Due Nov 12
[edit] Pick two
Pick two puzzles from FreePuzzles.com
[edit] DigitsToInt
Brian Smith wrote the following function, which converts a list of digits into an integer.
fun {DigitsToInt L}
{FoldL L fun {$ I J} {FD.plus {FD.times I 10} J} end 0}
end
It uses the fact that
ABCDE
as a list of digits may be interpreted as the integer
10*(10*(10*(10*A + B) + C) + D) + E
He used the FD operations as a way of constraining (and passing back and forth) the relationships between the digits and the resulting integer. If all one cares about is conversion, regular addition and multiplication work just as well.
fun {DigitsToInt L}
{FoldL L fun {$ I J} 10*I + J end 0}
end
For this homework, write another version of DigitsToInt that uses FoldR instead of FoldL. Take advantage of the fact that
ABCDE
as a list of digits may be interpreted as the integer
10^4*A + 10^3*B + 10^2*C + 10*D + E
One way to do this is to write the FoldR so that it passes a pair of values: (a) the power of 10 by which the next digit is to be multiplied and (b) the sum generated so far. Use this in a subsidiary function. The main function then takes the sum produced at the end and returns it as the integer.
If the top-level function is called with [1 2 3 4 5], the subsidiary funtion might produce results such as the following
10#5 100#45 1000#345 10000#2345 100000#12345
The top-level function would then return 12345.
[edit] Homework submissions
[edit] Due Nov 19
The jugs puzzles are classics. See Measuring with Jugs for a discussion. The original is apparently this one.
Problem: Given a 5-liter jug, a 3-liter jug, and an unlimited supply of water, how do you measure out exactly 4 liters?
Solution: Fill the 5-liter jug. Empty 3 liters from the 5-liter jug into the 3-liter jug, leaving 2 liters. Empty the 3-liter jug. Pour the 2 liters from the 5-liter jug into the 3-liter jug. Fill the 5-liter jug and pour 1 liter from it into the 3-liter jug, filling it. That leaves 4 liters in the 5-liter jug.
One way to represent this puzzle is with a list of pairs, where each pair represents a jug. The two numbers in the pair are the capacity of the jug and the amount of water in it.
This problem solution might be represented as follows.
[5#0 3#0] Starting position. Both jugs are empty. [5#5 3#0] Fill the 5 liter jug. [5#2 3#3] Empty three liters from the 5-liter jug into the 3-liter jug. [5#2 3#0] Empty the 3-liter jug. [5#0 3#2] Pour the 5-liter jug into the 3-liter jug. [5#5 3#2] Fill the 5-liter jug. [5#4 3#3] Pour the 5-liter jug into the 3-liter jug leaving 4 liters in the 5-liter jug.
There are only three operations:
- Fill a jug.
- Empty a jug.
- Pour from one jug (the source jug) into another jug (the sink jug) until either the source jug is empty or the sink jug is full.
In any of these operations any of the jugs may be the one to be filled, the one to be emptied, or the one to act as source or sink. (The water doesn't have to go from left to right.)
In the general case there may be more than two jugs.
[edit] Write a program that will solve Jugs Puzzles
A reasonable approach is to have the program generate a list of configurations. Initially the list contains the starting configuration. It then generates additional configurations one-by-one, which it adds to the list. It continues adding configurations until a configuration is reached that contains the goal.
To go from one configuration to the next, apply one of the operations to the most recent configuration to generate a new configuration. When applying an operation there will be a number of choices.
- which operation should you apply?
- to which jugs should you apply the operation?
The program should make these decisions non-deterministically.
The program should have two parameters:
- an input configuration, which will be a configuration of the sort shown above, but with any number of jugs, with any capacities, and with any valid initial contents, i.e., not more than the capacity of the jug.
- a goal amount that some jug should contain in the final configuration.
The output will be a list of configurations starting with the given initial configuration and ending with a configuration having a jug with the goal contents.
If the program is named Jugs, it might be called as follows for the problem shown above.
{Jugs [5#0 3#0] 4}
The returned value if it is a function (or the third parameter if it is a procedure) should be:
[ [5#0 3#0] [5#5 3#0] [5#2 3#3] [5#2 3#0] [5#0 3#2] [5#5 3#2] [5#4 3#3] ]
It would be even nicer to generate some annotation along with each state (as in the illustration above) that indicates how that state was generated from the previous state.
[edit] Controlling the search
The problem with the proposed program is that we don't have any way to control the order in which the system explores the various options. It may, for example, explore the path that fills the 5-liter jug; then empties the 5-liter jug; then fills the 5-liter jug; then empties the 5-liter jug; etc.
This week, we'll use two techniques.
- Iterative deepening.
Include a third parameter, the maximum search depth. Do not let the list of configurations exceed this length. This is a way to ensure that a solution will be found within this depth if there is one.
If the 3-parameter program is called Jugs3, write a program called Jugs that calls Jugs3 but that has only the first two parameters. It should call Jugs3 first with a maximum depth parameter of 1. If no solution is found, call it again with a maximum depth parameter of 2, etc. until a solution is found.
See the discussion immediately below about how to use FD to implement this sort of iterative deepening search.
- Loop detection and avoidance. Loop detection and avoidance works for this problem because (a) the number of possible distinct states is small and (b) we are keeping track of previous states. The trick is simply to refuse to repeat a configuration. If a step produces a configuration that is already in the list of configurations, do not add it to the list. If a new configuration is the same as a previous one, either fail the process explicitly using the fail statement or simply don't proceed.
[edit] Using FD to perform an iterative deepening search
The map-coloring example in the FD tutorial uses FD to control an iterative deepening search. Here is a version of that program in which the iterative deepening aspect of it has been factored out.
% Uses iterative deepening to explore the function
% Function when applied to the argument Arg.
% The Function parameter must know how to use Limit
% to limit its exploration. In our example, Jugs3
% would know not to generate a sequence of
% configurations longer than Limit.
% Note also that the Function parameter has as
% a function as its value. We then call that
% function and pass it two arguments in the last
% line of this function. This is often not
% possible in traditional programming languages.
local
fun {IterativeDeepening Function Arg}
% Declare Limit to be an FD variable
% with range 0#134217726.
Limit = {FD.decl}
in
% Distribute over Limit: try a value of 0; if that fails,
% try a value of 1; if that failes, try a value of 2, etc.
{FD.distribute naive [Limit]}
% Pass (a value for) Limit to the Function to be
% explored along with its Arg.
{Function Arg Limit}
end
% Find a map-coloring using (no more than) NbColors.
fun {MapColoring Data NbColors}
% Extract the countries from Data
% Note that the formal parameter to the anonymous
% function is given as a tuple: C#_.
Countries = {Map Data fun {$ C#_} C end}
% Create a record that maps countries to colors.
% Coloring: Countries --> 1#NbColors
Coloring = {FD.record color Countries 1#NbColors}
in
{ForAll Data
% Note that the formal parameter to the anonymous
% proc is given as a tuple: A#Bs.
proc {$ C#Cs}
% Require that no country has the same color as a neighbor.
{ForAll Cs proc {$ Neigh} Coloring.C \=: Coloring.Neigh end}
end}
{FD.distribute ff Coloring }
Coloring
end
Data = [ austria # [italy switzerland germany] belgium # [france netherlands germany luxemburg] france # [spain luxemburg italy] germany # [austria france luxemburg netherlands] italy # nil % Don't have to repeat neighbor relations luxemburg # nil netherlands # nil portugal # nil spain # [portugal] switzerland # [italy france germany austria] ]
in
{ExploreOne fun {$} {IterativeDeepening MapColoring Data} end}
end
This program first distributes over Limit, which represents the number of colors. For each value, starting with 0, it attempts to do a map coloring using that number of colors. If it fails, it tries again with the next higher value.
To use this iterative deepening implementation for the jugs problem you will have to write a JugsID function that is called as follows.
{JusgID [5#0 3#0]#4 Limit}
The first argument combines the two arguments to the original Jugs function. The second argument tells JugsID the maximum length of the sequence of states to consider.
The more brute force approach to iterative deepening (without using FD) would require a loop in which SearchAll would be called with increasingly higher limit values until a limit value is found that returns a non-nil result. Using FD is presumably more efficient since the search mechanism doesn't have to be re-created and re-started repeatedly.
[edit] Homework submissions
[edit] A Simple Solution
Robert Ritchey points out that the problem is actually quite simple. The article I mentioned above (Ivars Peterson's MathTrek: Measuring with Jugs) notes that you can get any result up to the size of the larger jug if the two jugs have capacities that are relatively prime. (You can then fill the smaller jug to extend the range.) It also refers to another article (Boldi, Santini, and Vigna, Measuring with Jugs), which discusses the n-jug problem.
For two jugs, pick one jug to be the source and the other to be the sink. Fill the source jug and empty it into the sink jug, which you empty whenever it gets full. Eventually the source jug will have the right amount. This works because if the two capacities are relatively prime, say A and B with A > B, then for any value N < A, there are numbers X and Y so that A*X - B*Y = N.
Given two relatively prime numbers A and B, if you consider the following sequence:
0 (mod A),
B (mod A),
2*B (mod A),
3*B (mod A),
…
(A*B - 3*B) (mod A),
(A*B - 2*B) (mod A),
(A*B - B) (mod A),
you get all the numbers between 0 and A-1.
For example, if A and B are 5 and 3 respectively, you get
0 mod 5 = 0.
3 mod 5 = 3
6 mod 5 = 1
9 mod 5 = 4
12 mod 5 = 2
So by starting with 0 and repeatedly adding 3 (by pouring from the 3 into the 5, emptying the 5, and refilling the 3 as needed), one cycles though all the numbers between 0 and 4.
The same thing happens if you "pour" in the other direction. It just goes through the numbers in the reverse order.
It's so easy I'm surprised that this "puzzle" has remained a puzzle. I simply assumed that it wouldn't reduce to something so simple.
[edit] FD Solution
Here are 3 finite domain versions of the jugs programs. The first finds a solution rapidly and can be expanded to accommodate as many jugs as desired. The only changes that need to be made are to the portion where numerical values are calculated using constraints set by the programmer. This is in the jugs function, the last function in this file. In the second version, a function creates a for loop which encloses a Search statement. This allows the Jugs program to find a finite domain solution to the puzzle. The Search statement is modified by the keyword next. This, in combination with the loop construct, enables the program to find a set of numbers, then the next, and repeat this process if called for. This program shows that there are several numerical solutions for a given set of jugs. The third version uses choice to show that there are several combinations of pours possible with the set of numbers generated with finite domain. A choice statement in the function SmallestXT will choose a jug that will be poured into. It will also be called with, different parameters, to find the one that will be pouring. At the choice statement, a “choice” point is created which allows the program to cycle through all of the combinations of fills, pours, and empties. This program shows that several combinations of pours are possible with a given set of numbers. Together, these programs make the point that there are several solutions for a given set of jugs. The only constraints are common sense – don’t try to pour from an empty jug and don’t pour into a full one. The order in which the pours and fills are made really does not matter!
[edit] Due Dec 3 (final homework)
Formally, a difference list is a pair of lists in which the second list is a suffix (not necessarily a proper suffix) of the first.
nil#nil [a]#nil [a b]#[b] [a b c d]#[c d]
A difference list is understood to represent that part of the first list that is not included in the second. From the examples above:
nil [a] [a] [a b]
Difference lists are most interesting when the second list is a variable. In that case, the first list is a list whose final tail is the variable represented as the second list.
DiffList = (a | b | c | F)#F
Note that there is no way to represent the first list in standard list notation because standard list notation presumes that the final tail is nil.
To convert a difference list into the list that it represents, one must select only those elements of the first list that are not in the second. That's not a very interesting operation.
In the particular case when the second list is a variable, the resulting list can be found by setting that variable to nil. Thus if we let F = nil in the previous example, DiffList is now the list
DiffList = [a b c]#nil
which represents the list
[a b c]
Difference lists are useful when the second list is a variable because they allow one to access the tail of the list without traversing the entire list. Thus to add d to the end of the difference list
DL = (a | b | c | F)#F
all we need do is let
F = (d | G)#G
At this point we will have
DL = (a | b | c | d | G)#(d | G)
[edit] Enqueue
An enqueue operation on difference lists can be written as follows.
fun {Enqueue DL Elt}
NewFinalVar
in
case DL
of List#FinalVar then
FinalVar = Elt | NewFinalVar
List#NewFinalVar
end
end
This works because the final tail of List is FinalVar. Thus
{Enqueue {Enqueue (a | b | c | F)#F d} e}
produces
(a|b|c|d|e|G)#G
where G is a new variable.
[edit] Append
Append is also quick and easy with difference lists.
fun {DLAppend DL1 DL2}
% The p() record structure just holds the pieces together
% for pattern matching.
case p(DL1 DL2) of p(L1#V1 L2#V2) then
V1 = L2
L1#V2
end
end
{DLAppend (a | b | X)#X (c | d | e | Y)#Y}
produces
(a | b | c | d | e | Y)#Y
[edit] Assignment
- In the discussion of SolveAll we indicated that efficiency could be improved by using difference lists instead of lists. Rewrite SolveAll to use difference lists.
- Select (and write programs that will solve) two problems of your choice from Ask Dr. Math: "Getting Across the River" or Ivars Peterson's "Tricky Crossings". Decide whether to use a depth-first or breadth-first search. Even better, write a single program that will solve a wide variety (more than one? many? all?) of these problems given the right input parameters.
[edit] Sudoku using Finite Sets
The Mozart-oz mailing list recently included this message.
When I was thinking of writing a sudoku solver, I thought of treating the numbers as FS variables instead of treating the positions as FD variables. Rows, columns and blocks are (fixed) sets of positions as well.In other words, each cell in the grid would have a unique ID, e.g., 1 .. 81. Each number, row, column, and 3x3 subgrid would be a set of these with the constraints as indicated. Someone replied as follows.
Then you just need to say the numbers are all mutually disjoint, have cardinality 9, and have a singleton intersection with each row, column and block.
I though this would be nicer to code than the FD approach. Any thoughts on the relative merits of the approaches?
I tried this, but I cannot say it is nicer to code. One problem is that you need a lot more propagators than with integers: instead of 9 alldiff for the rows, 9 alldiff for the columns, and 9 alldiff for the blocks, you need 45 disjointness propagators, 81 intersections for the rows, 81 intersections for the columns, and 81 intersections for the blocks, plus cardinality restrictions for all intersections.It may be that you need all these constraints. But you should be able to generate them in the code. That may make the system run more slowly, but it would still be interesting to see how it looks. You will also have to figure out how to display the results.
In my experiments, the integer model also performed much better: it solved more puzzles without search, in much less time, and with a 5th of the memory. I can't say anything about formal properties like if the integer model is always stronger, though.
Furthermore, the additional, implied constraints Helmut Simonis proposed are quite hard to model in the set case, I think.
If you are interested in looking into it, the Finite Set constraint tutorial is here.
And here's a special offer for the Holiday Season. Anyone (or any group) who reads the tutorial, solves the Sudoku problem using Finite Sets, and presents your results in class December 2, will receive an A in the course. Doing this is just a matter of doing some work on your own. You have two weeks to do it, and you can do it in a group. This offer is open only to people who attended the individual review sessions November 5.
[edit] Solutions
Four solutions were created. They all use the approach described above. They differ in the degree to which commonalities have been factored out.
The students who did the work read the FS tutorial on their own, and they wrote these programs on their own (or in one case in a team of two).
They report that the FS tutorial was not easy to read; that the FS documentation has errors; and that there aren't enough easy examples to help one get started.
They also report that they spent a considerable amount of time experimenting with the FS functions just to determine what they do.
They suggest that if the Oz documentation were available as a wiki, users could add explanatory comments, examples, and corrections as needed.
- Perhaps easiest to read because everything is built explicitly: Jay Donnell and Kelly Breed
- Many of the commonalities have been factored out into loops: Jeff Bailey
- More commonalities have been factored out: Josh Cain
- Most elegant: Brian Smith
[edit] The N-Jugs problem
Implement Jeff & Josh's virtual jug solution to the Jugs problem. The program should partition the set of jugs into subsets so that the sum of the jug capacities in two of the subsets are mutually prime. (Note that there may be more than two subsets. All you need is for two of them to have mutually prime capacity sums and that one of those sums be at least as large as the goal.) Then the program should generate moves that solve the problem using the virtual jugs and translate those step into operations on the actual jugs.
The A offer applies to this problem also.

