美文网首页
计算机科学和Python编程导论-第13课

计算机科学和Python编程导论-第13课

作者: 瘦长的丰一禾 | 来源:发表于2018-08-20 21:16 被阅读45次

12.背包问题

我们有n种物品,物品j的重量为wj,价格为pj。
我们假定所有物品的重量和价格都是非负的。背包所能承受的最大重量为W。
如果限定每种物品只能选择0个或1个,则问题称为0-1背包问题。

# 图12-2 Item类
In [7]: class Item(object):
   ...:     def __init__(self, n, v, w):
   ...:         self.name = n
   ...:         self.value = v
   ...:         self.weight = w
   ...:     def getName(self):
   ...:         return self.name
   ...:     def getValue(self):
   ...:         return self.value
   ...:     def getWeight(self):
   ...:         return self.weight
   ...:     def __str__(self):
   ...:         result = '<' + self.name + ', ' + str(self.value)\
   ...:             + ', ' + str(self.weight) + '>'
   ...:         return result
   ...: 

In [8]: def value(item):
   ...:     return item.getValue()
   ...: 

In [9]: def weightInverse(item):
   ...:     return 1.0/item.getWeight()
   ...: 

In [10]: def density(item):
    ...:    return item.getValue()/item.getWeight()
    ...: 

# 图12-3 贪婪算法的实现
In [11]: def greedy(items, maxWeight, keyFunction):
    ...:    """假设Items是列表, maxWeight >= 0
    ...:    keyFunctions将物品元素映射为数值"""
    ...:    itemsCopy = sorted(items, key=keyFunction, reverse = True)
    ...:    result = []
    ...:    totalValue, totalWeight = 0.0, 0.0
    ...:    for i in range(len(itemsCopy)):
    ...:        if (totalWeight + itemsCopy[i].getWeight()) <= maxWeight:
    ...:            result.append(itemsCopy[i])
    ...:            totalWeight += itemsCopy[i].getWeight()
    ...:            totalValue += itemsCopy[i].getValue()
    ...:    return (result, totalValue)
    ...: 

# 图12-4 使用贪婪算法选择物品
In [12]: def buildItems():
    ...:    names = ['clock','painting','radio','vase','book','computer']
    ...:    values = [175,90,20,50,10,200]
    ...:    weights = [10,9,4,2,1,20]
    ...:    Items = []
    ...:    for i in range(len(values)):
    ...:        Items.append(Item(names[i], values[i], weights[i]))
    ...:    return Items
    ...: 

In [13]: def testGreedy(items, maxWeight, keyFunction):
    ...:    taken, val = greedy(items, maxWeight, keyFunction)
    ...:    print('Total value of items taken is', val)
    ...:    for item in taken:
    ...:        print(' ', item)
    ...:   

In [14]: def testGreedys(maxWeight = 20):
    ...:    items = buildItems()
    ...:    print('Use greedy by value to fill knapsack of size', maxWeight)
    ...:    testGreedy(items, maxWeight, value)
    ...:    print('\nUse greedy by weight to fill knapsack of size',
    ...:        maxWeight)
    ...:    testGreedy(items, maxWeight, weightInverse)
    ...:    print('\nUse greedy by density to fill knapsack of size',
    ...:        maxWeight)
    ...:    testGreedy(items, maxWeight, density)
    ...:  

In [15]: testGreedys()
Use greedy by value to fill knapsack of size 20
Total value of items taken is 200.0
  <computer, 200, 20>

Use greedy by weight to fill knapsack of size 20
Total value of items taken is 170.0
  <book, 10, 1>
  <vase, 50, 2>
  <radio, 20, 4>
  <painting, 90, 9>

Use greedy by density to fill knapsack of size 20
Total value of items taken is 255.0
  <vase, 50, 2>
  <clock, 175, 10>
  <book, 10, 1>
  <radio, 20, 4>

其中函数getBinaryRep()和genPowerset()是在9.3.6 指数复杂度首次出现

In [22]: def getBinaryRep(n, numDigits):
    ...:    """假设n和numDigits为非负整数
    ...:    返回一个长度为numDigits的字符串,为n的二进制表
    ...:    示"""
    ...:    result = ''
    ...:    while n > 0:
    ...:        result = str(n%2) + result
    ...:        n = n//2
    ...:    if len(result) > numDigits:
    ...:        raise ValueError('not enough digits')
    ...:    for i in range(numDigits - len(result)):
    ...:        result = '0' + result
    ...:    return result

In [23]: def genPowerset(L):
    ...:    """假设L是列表
    ...:    返回一个列表,包含L中元素所有可能的集合。例如,
    ...:    如果
    ...:    L=[1, 2],则返回的列表包含元素[1]、 [2]和[1,
    ...:    2]"""
    ...:    powerset = []
    ...:    for i in range(0, 2**len(L)):
    ...:        binStr = getBinaryRep(i, len(L))
    ...:        subset = []
    ...:        for j in range(len(L)):
    ...:            if binStr[j] == '1':
    ...:                subset.append(L[j])
    ...:        powerset.append(subset)
    ...:    return powerset
    ...: 

# 图12-5 0/1背包问题的暴力最优解
In [24]: 
    ...: def chooseBest(pset, maxWeight, getVal, getWeight):
    ...:    bestVal = 0.0
    ...:    bestSet = None
    ...:    for items in pset:
    ...:        itemsVal = 0.0
    ...:        itemsWeight = 0.0
    ...:        for item in items:
    ...:            itemsVal += getVal(item)
    ...:            itemsWeight += getWeight(item)
    ...:        if itemsWeight <= maxWeight and itemsVal > bestVal:
    ...:            bestVal = itemsVal
    ...:            bestSet = items
    ...:    return (bestSet, bestVal)
    ...: 
    ...: def testBest(maxWeight = 20):
    ...:    items = buildItems()
    ...:    pset = genPowerset(items)
    ...:    taken, val = chooseBest(pset, maxWeight, Item.getValue,
    ...:        Item.getWeight)
    ...:    print('Total value of items taken is', val)
    ...:    for item in taken:
    ...:        print(item)

In [25]: testBest()
Total value of items taken is 275.0
<clock, 175, 10>
<painting, 90, 9>
<book, 10, 1>

12.2 图的最优化问题
# 图12-7 节点和边
In [49]: class Node(object):
    ...:    def __init__(self, name):
    ...:        """假设name是字符串"""
    ...:        self.name = name
    ...:    def getName(self):
    ...:        return self.name
    ...:    def __str__(self):
    ...:        return self.name
    ...: 
    ...: class Edge(object):
    ...:    def __init__(self, src, dest):
    ...:        """假设src和dest是节点"""
    ...:        self.src = src
    ...:        self.dest = dest
    ...:    def getSource(self):
    ...:        return self.src
    ...:    def getDestination(self):
    ...:        return self.dest
    ...:    def __str__(self):
    ...:        return self.src.getName() + '->' + self.dest.getName()
    ...: 
    ...: class WeightedEdge(Edge):
    ...:    def __init__(self, src, dest, weight = 1.0):
    ...:        """假设src和dest是节点, weight是个数值"""
    ...:        self.src = src
    ...:        self.dest = dest
    ...:        self.weight = weight
    ...:    def getWeight(self):
    ...:        return self.weight
    ...:    def __str__(self):
    ...:        return self.src.getName() + '->(' + str(self.weight) + ')'\
    ...:        + self.dest.getName()
    ...: 
# 图12-8 Graph类和Digraph类
In [50]: class Digraph(object):
    ...:    #nodes是图中节点的列表
    ...:    #edges是一个字典,将每个节点映射到其子节点列表
    ...:    def __init__(self):
    ...:        self.nodes = []
    ...:        self.edges = {}
    ...:    def addNode(self, node):
    ...:        if node in self.nodes:
    ...:            raise ValueError('Duplicate node')
    ...:        else:
    ...:            self.nodes.append(node)
    ...:            self.edges[node] = []
    ...:    def addEdge(self, edge):
    ...:        src = edge.getSource()
    ...:        dest = edge.getDestination()
    ...:        if not (src in self.nodes and dest in self.nodes):
    ...:            raise ValueError('Node not in graph')
    ...:        self.edges[src].append(dest)
    ...:    def childrenOf(self, node):
    ...:        return self.edges[node]
    ...:    def hasNode(self, node):
    ...:        return node in self.nodes
    ...:    def __str__(self):
    ...:        result = ''
    ...:        for src in self.nodes:
    ...:            for dest in self.edges[src]:
    ...:                result = result + src.getName() + '->'\
    ...:                + dest.getName() + '\n'
    ...:        return result[:-1] #omit final newline
    ...: 
    ...: class Graph(Digraph):
    ...:    def addEdge(self, edge):
    ...:        Digraph.addEdge(self, edge)
    ...:        rev = Edge(edge.getDestination(), edge.getSource())
    ...:        Digraph.addEdge(self, rev)
    ...:   
# 图12-9 最短路径的深度优先搜索算法
In [51]: def printPath(path):
    ...:    """假设path是节点列表"""
    ...:    result = ''
    ...:    for i in range(len(path)):
    ...:        result = result + str(path[i])
    ...:        if i != len(path) - 1:
    ...:            result = result + '->'
    ...:    return result
    ...: 
    ...: def DFS(graph, start, end, path, shortest, toPrint = False):
    ...:    """假设graph是无向图; start和end是节点;
    ...:    path和shortest是节点列表
    ...:    返回graph中从start到end的最短路径。 """
    ...:    path = path + [start]
    ...:    if toPrint:
    ...:        print('Current DFS path:', printPath(path))
    ...:    if start == end:
    ...:        return path
    ...:    for node in graph.childrenOf(start):
    ...:        if node not in path: #avoid cycles
    ...:            if shortest == None or len(path) < len(shortest):
    ...:                newPath = DFS(graph, node, end, path, shortest,
    ...:                    toPrint)
    ...:            if newPath != None:
    ...:                shortest = newPath
    ...:    return shortest
    ...: 
    ...: def shortestPath(graph, start, end, toPrint = False):
    ...:    """假设graph是无向图; start和end是节点
    ...:    返回graph中从start到end的最短路径。 """
    ...:    return DFS(graph, start, end, [], None, toPrint)
    ...: 
In [52]: def testSP():
    ...:    nodes = []
    ...:    for name in range(6):
    ...:        nodes.append(Node(str(name)))
    ...:    g = Digraph()
    ...:    for n in nodes:
    ...:        g.addNode(n)
    ...:    g.addEdge(Edge(nodes[0],nodes[1]))
    ...:    g.addEdge(Edge(nodes[1],nodes[2]))
    ...:    g.addEdge(Edge(nodes[2],nodes[3]))
    ...:    g.addEdge(Edge(nodes[2],nodes[4]))
    ...:    g.addEdge(Edge(nodes[3],nodes[4]))
    ...:    g.addEdge(Edge(nodes[3],nodes[5]))
    ...:    g.addEdge(Edge(nodes[0],nodes[2]))
    ...:    g.addEdge(Edge(nodes[1],nodes[0]))
    ...:    g.addEdge(Edge(nodes[3],nodes[1]))
    ...:    g.addEdge(Edge(nodes[4],nodes[0]))
    ...:    sp = shortestPath(g, nodes[0],nodes[5],toPrint = True)
    ...:    print('Shortest path is',printPath(sp))
    ...:  
In [53]: testSP()
Current DFS path: 0
Current DFS path: 0->1
Current DFS path: 0->1->2
Current DFS path: 0->1->2->3
Current DFS path: 0->1->2->3->4
Current DFS path: 0->1->2->3->5
Current DFS path: 0->1->2->4
Current DFS path: 0->2
Current DFS path: 0->2->3
Current DFS path: 0->2->3->4
Current DFS path: 0->2->3->5
Current DFS path: 0->2->3->1
Current DFS path: 0->2->4
Shortest path is 0->2->3->5

丰一禾 2018/08/20 15:43:40
一、学习安排(8月16日-8月20日)
1.主要学习视频Week6
链接([图片上传失败...(image-8c50b5-1534751046074)]

http://www.xuetangx.com/courses/MITx/6_00_2x/2014_T2/courseware/d39541ec36564a88af34d319a2f16bd7/
2.参考书第十二章--背包与图的最优化问题

二、作业上传事项
1.此次作业内容包括几个方面:
(a)背包问题理解
(b)图最优化问题理解
(c)动态规划的理解
(提交形式,是以“汇报”形式汇报给助教(李凯旋),对于作业敷衍的直接视为下车)
2.作业提交日期
作业规定在8月20日24点之前,大家把握好时间,且8月21日晚上,助教会公布没有交作业者并令其下车;
@所有人

相关文章

网友评论

      本文标题:计算机科学和Python编程导论-第13课

      本文链接:https://www.haomeiwen.com/subject/ysjliftx.html