def makeChange(Q, D): ## Helper functions - begin ###################### def getValue(n): p = T.parent(n) if(p == None): return T.getContents(n) else: return T.getContents(n) + getValue(p) def shouldBeAdded(n): return (not getValue(n) > Q) # This function returns the list of new partial solutions that can be created with a value x. # It just returns a list with only one element indicating increment of sum by x. def process(x): return [x] # Given a partial solution s, extend it by one step using each of the denominations available in D. extendSolution = bfs.makeExtendSolution(D, process, shouldBeAdded) def isSolution(n): return getValue(n) == Q ## Helper functions - end ###################### ## Main algorithm - begin ###################### return bfs.search(0, extendSolution, isSolution)
def fillJug(capacities, Q): INITACTION = 0 EMPTYACTION = 1 FILLACTION = 2 TRANSFERACTION = 3 # Given a list of incremental contents, this function computes the actual contents at every step. # Example: if contents = [(FILLACTION, 1), (FILLACTION, 2), (TRANSFERACTION, (2, 0)] the output # for capacities=[3, 4, 5] is [[0, 4, 0], [0, 4, 5], [3, 4, 5]] # The last element of this output list is the final actual contents of the jugs. def getValueFromContents(contents): values = [] value = [0] * len(capacities) for content in contents: if(content[0] == EMPTYACTION): value = empty(content[1], value) elif(content[0] == FILLACTION): value = fill(content[1], value) elif(content[0] == TRANSFERACTION): value = transfer(content[1][0], content[1][1], value) else: pass values.append(value) return values # This calls the getValueFromContents function to obtain the sequence of actual contents, and # returns the last element of this list, which is the current actual contents. def getValue(n): ntrace = T.getTrace(n) contents = [T.getContents(n1) for n1 in ntrace] return getValueFromContents(contents)[-1] def isSolution(s): def numOfNonEmptyJugs(c): def g(x, y): if(y == 0): return x return x + 1 return reduce(g, c, 0) def totalQuantity(c): return reduce(lambda x, y: x + y, c, 0) v = getValue(s) return (numOfNonEmptyJugs(v) == 1) and (totalQuantity(v) == Q) # empty the ith jug. def empty(i, contents): newcontents = contents[:] newcontents[i] = 0 return newcontents # fill the ith jug to its capacity def fill(i, contents): newcontents = contents[:] newcontents[i] = capacities[i] return newcontents # transfer the contents of jug i to jug j. Do it only if: # -- i is not empty # -- j is not full # Transfer only so much as j can hold (in case i holds more fluid than j can take) def transfer(i, j, contents): def min(x, y): if(x < y): return x return y def isEmpty(i): return contents[i] == 0 def isFull(i): return contents[i] == capacities[i] newcontents = contents[:] if(not (isEmpty(i) or isFull(j))): q = min(contents[i], capacities[j] - contents[j]) newcontents[i] -= q newcontents[j] += q return newcontents def process(i): nss = [] nss.append((EMPTYACTION, i)) nss.append((FILLACTION, i)) for j in range(len(capacities)): if(i != j): nss.append((TRANSFERACTION, (i, j))) return nss # Determine whether a node with the same contents as node n already exists. def isNewContents(n): return not T.searchNode(n, getValue) # Criterion for adding a particular configuration into the BFS queue: The set of jug-contents should never # have happened so far. def shouldBeAdded(n): return isNewContents(n) # The first argument is the range of jug indexes. extendSolution = bfs.makeExtendSolution(range(len(capacities)), process, shouldBeAdded) result = bfs.search((INITACTION, None), extendSolution, isSolution) return getValueFromContents(result)
def fillJug(capacities, Q): INITACTION = 0 EMPTYACTION = 1 FILLACTION = 2 TRANSFERACTION = 3 def getValueFromContents(contents): values = [] value = [0] * len(capacities) for content in contents: if(content[0] == EMPTYACTION): value = empty(content[1], value) elif(content[0] == FILLACTION): value = fill(content[1], value) elif(content[0] == TRANSFERACTION): value = transfer(content[1][0], content[1][1], value) else: pass values.append(value) return values # def getValue(n): ntrace = T.getTrace(n) contents = [T.getContents(n1) for n1 in ntrace] return getValueFromContents(contents)[-1] def isSolution(s): def numOfNonEmptyJugs(c): def g(x, y): if(y == 0): return x return x + 1 return reduce(g, c, 0) def totalQuantity(c): return reduce(lambda x, y: x + y, c, 0) v = getValue(s) return (numOfNonEmptyJugs(v) == 1) and (totalQuantity(v) == Q) # empty the ith jug. def empty(i, contents): newcontents = contents[:] newcontents[i] = 0 return newcontents # fill the ith jug to its capacity def fill(i, contents): newcontents = contents[:] newcontents[i] = capacities[i] return newcontents # transfer the contents of jug i to jug j. Do it only if: # -- i is not empty # -- j is not full # Transfer only so much as j can hold (in case i holds more fluid than j can take) def transfer(i, j, contents): def min(x, y): if(x < y): return x return y def isEmpty(i): return contents[i] == 0 def isFull(i): return contents[i] == capacities[i] newcontents = contents[:] if(not (isEmpty(i) or isFull(j))): q = min(contents[i], capacities[j] - contents[j]) newcontents[i] -= q newcontents[j] += q return newcontents def process(i): nss = [] ns = (EMPTYACTION, i) nss.append(ns) ns = (FILLACTION, i) nss.append(ns) for j in range(len(capacities)): if(i != j): ns = (TRANSFERACTION, (i, j)) nss.append(ns) return nss # Determine whether a node with the same contents as node n already exists. def isNewContents(n): return not T.searchNode(n, getValue) # Criterion for adding a particular configuration into the BFS queue: The set of jug-contents should never # have happened so far. def shouldBeAdded(n): return isNewContents(n) extendSolution = bfs.makeExtendSolution(range(len(capacities)), process, shouldBeAdded) ##### extendSolution - End ################## result = bfs.search((INITACTION, None), extendSolution, isSolution) return getValueFromContents(result)