Consider 3 water jugs 1,2,3 with capacities 12,8,5 respectively. The total water held by the 3 jugs should be 12 units. We can either empty jug x into jug y (xEy) or fill up jug y from jug x (xFy). With these considerations in mind, given a start state and a goal state, how can we tell if the goal state is reachable in finite steps without actually performing the algorithm?
The equation x+y+z = 12 has 53 solutions, i.e., 2756 possible pairs of start and goal states. Inspecting all these pairs for reachability individually, even on a computer is madness, like the goal state (0,7,5) has 12 start states for which it is reachable. Or I have not yet thought of a good algorithm. Anyways, the number is too big and I want some 'thumb rule' for identifying reachable states. Any help or idea would be appreciated.
Let's uniquely identify a jug state as the amount of water in all 3 jugs with this little data structure (an array of 3 integers). For this exercise, I'll code in C/C++, but it could easily be ported to any programming language.
So for example, if the first jug has 4 units of water, the second jug has 5, and the third jug has 3. Then we can have a JugState above of
{4,5,3}for example.But given that the jugs have a fixed capacity of 12, 8, and 5 respectively. We can easily have an integer representation of the jug state. The above {4,5,3} configuration could easily be represented with the number
453. A jug configuration of{11,0,1}could be represented as1101. It doesn't have to be a decimal encoding, but it makes for debugging easier.We can have helper functions that converts from JugState to "jug state index".
Now we can define a function that moves water from one jug to another. This function is aware of the 12,8,and 5 unit capacities of each jug. It takes as parameters the initial "jug state index", and two parameters to indicate where water is being moved from (e.g. 0,1,2 indices of the JugState). For example, if you invoke it as
moveWater(453, 0, 1)that's basically saying "move water from the first jug indo the second jug". It should return in this case,183since only 3 units from the first jug can be poured into the second.Now we can think of this problem as a graph. Given any state such as
{4,5,3}(index == 453) and we want to test if it's possible to transition to{7,1,4}(index == 714). At any given state, there are 6 valid transitions that can be made. (i.e. move water from jug0->jug1, jug0->jug2, ..., jug2->jug0). So we can just use asetto keep track of what node indices we've visited so that we don't recurse indefinitely. The cool part of this tree traversal is that we don't need to build an actual tree.Now let's introduce 2 helper functions. One function to help us build up that initial set of 53 valid jug states with 12 units of water. This function is aware of the 12/8/5 limits on the jugs. But you can pass it any target state amount you want.
And a helper function to make a string representation of a jug state:
Now we have enough to code up a test for all 2756 combinations of transitions for the 12 units of water configurations.
Run our code (here's a snippet)...
You can get the complete listing here:
https://github.com/jselbie/threejugproblem/blob/main/main.cpp