一個裝滿8升酒的瓶子,另外只有一個5升的空瓶子和一個3升的空瓶子,問怎樣倒可以把酒一絲不差的分成兩個4升?(當然不可以直接比較水平面,瓶子的底面半徑不同)
學習MIT的公開課計算機科學入門課程6.001x和6.002x將近一個學期了,我幾乎把它當作第一重要的事情,每天早晨第一件事就是學習這門公開課。我確信我有很大的收獲,正如他們的宗旨用計算思維解決真實問題using computation to solve real problem,我想計算思維是學習這門課最大的收獲。
最近看書,碰巧看到一個題目
“一個裝滿8升酒的瓶子,另外只有一個5升的空瓶子和一個3升的空瓶子,問怎樣倒可以把酒一絲不差的分成兩個4升?”
像是一道小學的奧數題,沒准當年我遇到過,讓我苦惱過。
現在看來題目還是趣味十足,但也難度十足,如果真要我做,我想我肯定能做出來,因為我這么多年的學習讓我感到,面對再困難的問題,去嘗試就有可能得到解答。
但現在嘛,我變得更不一樣了,我可以用算法來解決這個問題。現在我知道這可以看成是一個圖搜索問題,每一個節點是酒的一種狀態,每一條邊是倒酒所導致狀態的切換。
選擇廣度優先搜索,第一個給出的答案是操作次數最短的。圖根據搜索過程動態產生節點。
程序一眨眼就運行完了,沒想到總共找到了16種結果,但每一種看起來都不太一樣。最少的一種需要操作7次,quite FUN!
實現程序的一個關鍵問題是,如何倒酒,現實情況是你知道哪個瓶子是滿的,哪個瓶子還剩多少容量。往一個滿的瓶子倒酒沒有意義,從一個空瓶倒酒出來也沒有意義。
我的做法是,嘗試從任意一個瓶子倒到另外一個瓶子里,只有6種可能。是否可以倒酒,由源酒瓶和目標酒瓶酒的容量決定。如果源酒瓶酒太多,目標酒瓶裝不下,則目標酒瓶倒滿;否則源酒瓶的酒太少,則把酒倒光。如果嘗試去倒,各瓶酒的狀態卻未變,證明不可行,即停止繼續嘗試倒酒。
Python代碼
volume = [8, 5, 3] class State(object): def __init__(self, one, two, three): self.v = [one, two, three] def transition(self, src, to): newV = self.v[:] #caution list is mutable # if src bottle don't have enough liqur, pour all if newV[src] < volume[to] - newV[to]: pour = newV[src] # if to bottle don't have enough space, fill it else: pour = volume[to] - newV[to] newV[src] -= pour newV[to] += pour return State(newV[0],newV[1],newV[2]) def __eq__(self, other): return self.v == other.v def __str__(self): return "%s %s %s" % (self.v[0], self.v[1], self.v[2]) def BFSWithGeneratorAll(start, end, q=[]): initPath = [start] q.append(initPath) paths = [] while len(q) > 0: tmpPath = q.pop(0) lastNode = tmpPath[-1] if lastNode == end: paths.append(tmpPath) for (src, to) in zip([0,0,1,1,2,2],[1,2,0,2,0,1]): new = lastNode.transition(src,to) if new not in tmpPath: newPath = tmpPath + [new] q.append(newPath) return paths def printSolution(path): for elt in path: print elt start = State(8,0,0) end = State(4,4,0) paths = BFSWithGeneratorAll(start,end) for path in paths: printSolution(path) print
找到的全部結果,每一行是酒的一種狀態。起始狀態是8L的壺里有8L酒,最終的狀態是8L和5L的壺里各有4L酒。
8L 5L 3L 8 0 0 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 4 4 0 8 0 0 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 4 1 3 4 4 0 8 0 0 5 0 3 0 5 3 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 4 4 0 8 0 0 5 0 3 5 3 0 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 4 4 0 8 0 0 3 5 0 0 5 3 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 4 1 3 4 4 0 8 0 0 3 5 0 3 2 3 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 4 1 3 4 4 0 8 0 0 3 5 0 3 2 3 0 5 3 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 4 1 3 4 4 0 8 0 0 5 0 3 5 3 0 2 3 3 0 5 3 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 4 4 0 8 0 0 5 0 3 5 3 0 2 3 3 2 5 1 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 4 4 0 8 0 0 3 5 0 3 2 3 6 2 0 6 0 2 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 4 1 3 4 4 0 8 0 0 5 0 3 5 3 0 2 3 3 2 5 1 0 5 3 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 4 4 0 8 0 0 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 4 4 0 8 0 0 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 0 5 3 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 4 1 3 4 4 0 8 0 0 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 4 1 3 4 4 0 8 0 0 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 0 5 3 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 4 1 3 4 4 0 8 0 0 5 0 3 5 3 0 2 3 3 2 5 1 7 0 1 7 1 0 4 1 3 0 5 3 3 5 0 3 2 3 6 2 0 6 0 2 1 5 2 1 4 3 4 4 0