关于编程之美中买书的问题,我困扰了好久。当然,我的数学能力不强,领悟能力又差,所以才困扰了这么久。不过,在本人的坚持下,终于理解透彻。为了记住这一时刻,更为了免除以后忘记的可能,暂时记录如下。
书中关于证明F(y1-1,y2-1,y3-1,y4-1,y5)和F(y1-1,y2-1,y3-1,y4,y5-1)的等价性,作者举了一个例子,y1=y2=y3=y4=y5=2。但我认为,这个例子太过特殊,因为每卷本的数量都是相同的,显然只要调整后两项的顺序,(y1-1,y2-1,y3-1,y4-1,y5)和(y1-1,y2-1,y3-1,y4,y5-1)的序列便是完全一样的,这个是很好理解的,根本不需要再进行多余的解释。但是如果y4和y5不相等的话,就需要多解释一下了。我想,作者真是企图解释这种情况,但是却用了一个特殊的不用多做解释的例子。其次,我想,除了完全归纳法之外,通过举具体例子的方式去证明一个抽象的问题是不足的。那么应该如何证明呢?
其实,我也不知道怎么证明才好,只能根据自己的理解做一下逻辑推理,当然这个推理基于书中的推理但又做了延伸。让我们来看(y1-1,y2-1,y3-1,y4,y5-1)这个序列,由于y4>=y5, 所以y4>y5-1,那么在最后的最优解中,一定会出现只有y4卷而没有y5卷的一组(至少一组,可能多组,根据y4和y5的相对大小和最优解的分配情况)。那么把这个只有y4卷而没有y5卷的一组(只是一组,而不是多组,我想这一点书中作者没有说明清楚)中的y4卷换成y5卷后,得到的仍是同样的解,但是由于整体上看,y4卷少了一本,y5卷多了一本,这正是(y1-1,y2-1,y3-1,y4-1,y5)的序列。这时,可以回归书中的一句话(y1-1,y2-1,y3-1,y4,y5-1)的最优解必是(y1-1,y2-1,y3-1,y4-1,y5)的一个解,因此只要求出(y1-1,y2-1,y3-1,y4-1,y5)的最优解就好。
好了,对书中的这个内容作了补充解释之后,再来个声明:以上的推理也好,解释也好,都是以书中的解释为基础,加以引申的,也许不应该叫半原创吧。
上代码时间:
#!/usr/bin/env python
import random
def getMinPrice(books):
books.sort()
books.reverse()
if books[0] <= 0:
return 0,[]
prices = []
paths = []
if books[4] >= 1:
price,path = getMinPrice([books[0]-1,books[1]-1,books[2]-1,books[3]-1,books[4]-1])
prices.append(5*8*(1-0.25)+price)
paths.append(path)
if books[3] >= 1:
price,path = getMinPrice([books[0]-1,books[1]-1,books[2]-1,books[3]-1,books[4]])
prices.append(4*8*(1-0.2)+price)
paths.append(path)
if books[2] >= 1:
price,path = getMinPrice([books[0]-1,books[1]-1,books[2]-1,books[3],books[4]])
prices.append(3*8*(1-0.1)+price)
paths.append(path)
if books[1] >= 1:
price,path = getMinPrice([books[0]-1,books[1]-1,books[2],books[3],books[4]])
prices.append(2*8*(1-0.05)+price)
paths.append(path)
if books[0] >= 1:
price,path = getMinPrice([books[0]-1,books[1],books[2],books[3],books[4]])
prices.append(8+price)
paths.append(path)
minPrice = min(prices)
i = prices.index(minPrice)
minPath = paths[i]
minPath.insert(0,len(prices)-i)
return minPrice,minPath
def getMinBookPrice(books):
assert(type(books) == list)
assert(len(books) == 5)
for i in books:
assert(type(i) == int)
return getMinPrice(books)
def getMinBookPrice2(books):
assert(type(books) == list)
assert(len(books) == 5)
for i in books:
assert(type(i) == int)
path = []
count5 = 0
count3 = 0
while books[0] > 0:
books.sort()
books.reverse()
if books[4] >= 1:
books = [books[0]-1,books[1]-1,books[2]-1,books[3]-1,books[4]-1]
path.append(5)
count5 += 1
elif books[3] >= 1:
books = [books[0]-1,books[1]-1,books[2]-1,books[3]-1,books[4]]
path.append(4)
elif books[2] >= 1:
books = [books[0]-1,books[1]-1,books[2]-1,books[3],books[4]]
path.append(3)
count3 += 1
elif books[1] >= 1:
books = [books[0]-1,books[1]-1,books[2],books[3],books[4]]
path.append(2)
else:
books = [books[0]-1,books[1],books[2],books[3],books[4]]
path.append(1)
if count5 > 0 and count3 > 0:
path.remove(5)
path.remove(3)
path.append(4)
path.append(4)
count5 -= 1
count3 -= 1
path.sort()
path.reverse()
price = 0
for i in path:
if i == 5:
price += 5*8*(1-0.25)
elif i ==4:
price += 4*8*(1-0.2)
elif i == 3:
price += 3*8*(1-0.1)
elif i == 2:
price += 2*8*(1-0.05)
else:
price += 8
return price,path
if __name__ == '__main__':
books = [random.randint(1,7) for i in range(5)]
print getMinBookPrice(books)
print getMinBookPrice2(books)
getMinBookPrice是归并法,getMinBookPrice2是贪婪法的修正实现,我发现测试了几次两个方法的结果是一致的。不过还是那句话,举例不足以证明贪婪法的修正实现的正确性。