使用圖的遍歷解決分酒問題


一個裝滿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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM