• Emily Zhang
  • Fiddler

Can You Pour the Water?

2024 Mar 292024 March 29 Problem Back

This Week's Fiddler

To get to 5 liters, we first fill pitcher A with 10 liters. We then dump 3 liters from A into and out of B; we do this three times to have 1 liter left in A. Pour this liter into B, and refill A. Now we can pour just 2 liters into B to fill it up, leaving us with 8 liters in A. Pour out B and pour 3 liters from A into B.

12 steps of modular arithmetic (and many words) later and we're left with 5 liters in A, completing our goal!

Maybe an animation will help drive the point home better:

10 liters

3 liters

Step 0


Extra Credit

Lots of numbers to crunch through, so let's let code do the heavy lifting for us.

We can represent the state of the pitchers by how full they are (call these aaa and bbb, where 0≤a≤A0\le a\le A0≤a≤A and 0≤b≤B0\le b\le B0≤b≤B for capacities AAA and BBB), and each action (e.g., adding water, moving water from one pitcher to the other, etc.) is like a state change in a finite state machine.

To minimize the number of steps it takes to get from the initial state (a=0a=0a=0 and b=0b=0b=0) to a successful state (a=Na=Na=N or b=Nb=Nb=N), we can perform a breadth-first search of this state machine. Start with a queue q of just the initial state, add all possible actions to the queue (if they haven't been seen yet), and mark these states as seen. We want to mark cases we've seen as we don't want to waste time exploring states we've already considered.

To keep track of how many steps we've taken, we will set steps = 0 and rem = len(q), designating the next len(q) iterations as all being equally far away from the initial state. After every iteration, we decrement rem, and once rem == 0, we increment steps and reset rem = len(q).

For the sake of curiosity (and visualization!) let's also add in a trace_path option which will traverse the order of actions that got us to our goal.

The Code (including an extra bit for part 1)
from collections import deque


def solve(A: int, B: int, N: int, trace_path: bool = False) -> int:
    parents: dict[tuple[int, int], tuple[int, int] | None] = {(0, 0): None}
    q: deque[tuple[int, int]] = deque(parents)
    rem = len(q)
    steps = 0
    while q:
        a, b = q.popleft()
        if a == N or b == N:
            if trace_path:
                curr = (a, b)
                s = deque()
                while curr is not None:
                    s.appendleft(list(curr))
                    curr = parents[curr]
                print(list(s))
            return steps
        cands = []
        if a < A:
            cands.append((A, b))
        if b < B:
            cands.append((a, B))
        if a > 0:
            cands.append((0, b))
            if b < B:
                if a + b <= B:
                    cands.append((0, a + b))
                else:
                    cands.append((a + b - B, B))
        if b > 0:
            cands.append((a, 0))
            if a < A:
                if a + b <= A:
                    cands.append((a + b, 0))
                else:
                    cands.append((A, a + b - A))
        for cand in cands:
            if cand not in parents:
                parents[cand] = (a, b)
                q.append(cand)
        rem -= 1
        if rem == 0:
            rem = len(q)
            steps += 1
    raise RuntimeError

print('Path:', end=' ')
print(solve(10, 3, 5, trace_path=True))
print()

A = 100
B = 93
results = [(solve(A, B, N), N) for N in range(1, 101)]
for steps, n in results:
    print(f'{n: >3}: {steps}')
print('Steps for all N:', [steps for steps, _ in results])
print()

max_steps, max_n = max(results)
print('Max steps:', max_steps)
print('N:', max_n)
print('Path:', end=' ')
solve(A, B, max_n, trace_path=True)
Output
Path: [[0, 0], [10, 0], [7, 3], [7, 0], [4, 3], [4, 0], [1, 3], [1, 0], [0, 1], [10, 1], [8, 3], [8, 0], [5, 3]]
12

  1: 164
  2: 52
  3: 110
  4: 106
  5: 56
  6: 160
  7: 2
  8: 168
  9: 48
 10: 114
 11: 102
 12: 60
 13: 156
 14: 6
 15: 172
 16: 44
 17: 118
 18: 98
 19: 64
 20: 152
 21: 10
 22: 176
 23: 40
 24: 122
 25: 94
 26: 68
 27: 148
 28: 14
 29: 180
 30: 36
 31: 126
 32: 90
 33: 72
 34: 144
 35: 18
 36: 184
 37: 32
 38: 130
 39: 86
 40: 76
 41: 140
 42: 22
 43: 188
 44: 28
 45: 134
 46: 82
 47: 80
 48: 136
 49: 26
 50: 190
 51: 24
 52: 138
 53: 78
 54: 84
 55: 132
 56: 30
 57: 186
 58: 20
 59: 142
 60: 74
 61: 88
 62: 128
 63: 34
 64: 182
 65: 16
 66: 146
 67: 70
 68: 92
 69: 124
 70: 38
 71: 178
 72: 12
 73: 150
 74: 66
 75: 96
 76: 120
 77: 42
 78: 174
 79: 8
 80: 154
 81: 62
 82: 100
 83: 116
 84: 46
 85: 170
 86: 4
 87: 158
 88: 58
 89: 104
 90: 112
 91: 50
 92: 166
 93: 1
 94: 162
 95: 56
 96: 108
 97: 110
 98: 54
 99: 164
100: 1
Steps for all N: [164, 52, 110, 106, 56, 160, 2, 168, 48, 114, 102, 60, 156, 6, 172, 44, 118, 98, 64, 152, 10, 176, 40, 122, 94, 68, 148, 14, 180, 36, 126, 90, 72, 144, 18, 184, 32, 130, 86, 76, 140, 22, 188, 28, 134, 82, 80, 136, 26, 190, 24, 138, 78, 84, 132, 30, 186, 20, 142, 74, 88, 128, 34, 182, 16, 146, 70, 92, 124, 38, 178, 12, 150, 66, 96, 120, 42, 174, 8, 154, 62, 100, 116, 46, 170, 4, 158, 58, 104, 112, 50, 166, 1, 162, 56, 108, 110, 54, 164, 1]

Max steps: 190
N: 50
Path: [[0, 0], [0, 93], [93, 0], [93, 93], [100, 86], [0, 86], [86, 0], [86, 93], [100, 79], [0, 79], [79, 0], [79, 93], [100, 72], [0, 72], [72, 0], [72, 93], [100, 65], [0, 65], [65, 0], [65, 93], [100, 58], [0, 58], [58, 0], [58, 93], [100, 51], [0, 51], [51, 0], [51, 93], [100, 44], [0, 44], [44, 0], [44, 93], [100, 37], [0, 37], [37, 0], [37, 93], [100, 30], [0, 30], [30, 0], [30, 93], [100, 23], [0, 23], [23, 0], [23, 93], [100, 16], [0, 16], [16, 0], [16, 93], [100, 9], [0, 9], [9, 0], [9, 93], [100, 2], [0, 2], [2, 0], [2, 93], [95, 0], [95, 93], [100, 88], [0, 88], [88, 0], [88, 93], [100, 81], [0, 81], [81, 0], [81, 93], [100, 74], [0, 74], [74, 0], [74, 93], [100, 67], [0, 67], [67, 0], [67, 93], [100, 60], [0, 60], [60, 0], [60, 93], [100, 53], [0, 53], [53, 0], [53, 93], [100, 46], [0, 46], [46, 0], [46, 93], [100, 39], [0, 39], [39, 0], [39, 93], [100, 32], [0, 32], [32, 0], [32, 93], [100, 25], [0, 25], [25, 0], [25, 93], [100, 18], [0, 18], [18, 0], [18, 93], [100, 11], [0, 11], [11, 0], [11, 93], [100, 4], [0, 4], [4, 0], [4, 93], [97, 0], [97, 93], [100, 90], [0, 90], [90, 0], [90, 93], [100, 83], [0, 83], [83, 0], [83, 93], [100, 76], [0, 76], [76, 0], [76, 93], [100, 69], [0, 69], [69, 0], [69, 93], [100, 62], [0, 62], [62, 0], [62, 93], [100, 55], [0, 55], [55, 0], [55, 93], [100, 48], [0, 48], [48, 0], [48, 93], [100, 41], [0, 41], [41, 0], [41, 93], [100, 34], [0, 34], [34, 0], [34, 93], [100, 27], [0, 27], [27, 0], [27, 93], [100, 20], [0, 20], [20, 0], [20, 93], [100, 13], [0, 13], [13, 0], [13, 93], [100, 6], [0, 6], [6, 0], [6, 93], [99, 0], [99, 93], [100, 92], [0, 92], [92, 0], [92, 93], [100, 85], [0, 85], [85, 0], [85, 93], [100, 78], [0, 78], [78, 0], [78, 93], [100, 71], [0, 71], [71, 0], [71, 93], [100, 64], [0, 64], [64, 0], [64, 93], [100, 57], [0, 57], [57, 0], [57, 93], [100, 50]]

Thus, with a whopping f(N) = 190 steps, N = 50 liters takes the cake for being the most time-consuming volume to achieve!

What does this data look like for all the NNN from 1 to 100?

Seems like there's a bit of a periodicity of 7, which makes sense as we can get 7 liters of water by taking pouring out 93 liters from pitcher A.

Finally, an animation of N=50N=50N=50 to go alongside it!

100 liters

93 liters

Step 0

— Emily

Back to Fiddler list
© 2024 Emily Bradon Zhang·Support Gaza Recovery