算法——动态规划

行和列的思想:

from collections import namedtuple
from pprint import pprint

"""
动态规划算法
旅行行程最大化:
    假设要去伦敦独家,假期是两天,但是想去的游览地点很多,你没发前往每一个地方游览,因此你需要列一个清单。
    对于想起的名声景点,都列出所需的时间和景点的推荐指数,设计算法,在指定的时间内将此次旅行规划到指数最大化
背包问题:
    假设你是一个小偷,你的背包只能装4磅的物品,现有一个可偷清单,分别标有物品的重量和价值,设计算法实现偷窃的价值最大化
野营问题:
    假设你要出克玩,你的背包容量为6磅,需要决定携带哪些物品,实现价值最大化

附加:
    1. 有些算法并非精确地解决步骤,而只是帮助你理清思路的框架

    2. 相同的系数,最终保留的是偏后者的,就是清单里面靠后的,所以相同系数自己比较喜欢的,要写在后面
"""


item = namedtuple('项目', ['name', 'requirement', 'coefficient'])

def main(data, max_requirement):
    """
    data:计划清单
    max_requirement: 预计最大条件
    """
    # 统计列数
    requirements = [i.requirement for i in data]
    min_col = min(requirements)
    col_num = int(max_requirement // min_col)
    row_num = len(data)
    # 初始化数据结构
    graph = [[{'total': 0, 'item': []} for j in range(col_num)] for i in range(row_num)]
    # 列
    col_context = [min_col+min_col*i for i in range(col_num)]


    def get_col_index_item_without_self(row, rest_index, name):
        """从下往上(先出的结果就是最大的系数项),获取一个不包含自己的item"""
        item = {'total': 0, 'item': []}
        for r in range(row, -1, -1):
            if name not in graph[r][rest_index]['item']:
                return graph[r][rest_index]
        return item
            

    # 开始动态规划
    for row in range(row_num):
        for col in range(col_num):
            name = data[row].name # 当前位置的条件值
            requirement = data[row].requirement # 当前位置的条件值
            coefficient = data[row].coefficient # 当前位置的系数值
            # 如果当前条件小于等于对应列
            if requirement <= col_context[col]:
                # 假设用当前行内容条件填充,查看是否还有剩余
                rest = col_context[col] - requirement
                if rest:
                    # 有剩余的话
                    rest_index = col_context.index(rest)
                    # 查看剩余的对应列,当前行开始,行为阶梯,往上走
                    max_item = get_col_index_item_without_self(row, rest_index, name)
                    # 这就是当前内容+剩余的最大系数值,简称sum
                    maybe_max = coefficient + max_item['total']
                    # 将这个sum和row-1,col比较,高则合并,低则自己
                    same_col_top_row_item = graph[row and row-1 or 0][col]
                    if maybe_max >= same_col_top_row_item.get('total'):
                        graph[row][col] = {
                            'total': max_item['total'] + coefficient,
                            'item': max_item['item'] + [name]
                        }
                    elif maybe_max < same_col_top_row_item['total']:
                        graph[row][col] = same_col_top_row_item
                else:
                    # 没有剩余
                    # 就看当前内容会不会比同列上一行的系数高,高则用,低则自己
                    top_row_item = graph[row and row-1 or 0][col]
                    if top_row_item['total'] > coefficient:  # 高则用
                        graph[row][col] = top_row_item
                    else:  # 低则自己
                        graph[row][col] = {'total': coefficient, 'item': [name]}
            else:  # 当前条件大于对应列
                if row == 0:  # 第一行就直接赋空
                    graph[row][col] = {'total': 0, 'item': []}
                # 不是第一行就取等列上一行的数据
                else:
                    graph[row][col] = graph[row-1][col]

    return graph


if __name__ == '__main__':
    # 旅行行程最大化
    check_list = [
        item('威斯敏斯特教堂', 0.5, 7),
        item('环球剧场', 0.5, 6),
        item('英国国家美术馆', 1, 9),
        item('大英博物馆', 2, 9),
        item('圣保罗大教堂', 0.5, 8),
    ]
    max_requirement = 2
    """
    [{'item': ['威斯敏斯特教堂'], 'total': 7},
    {'item': ['威斯敏斯特教堂'], 'total': 7},
    {'item': ['威斯敏斯特教堂'], 'total': 7},
    {'item': ['威斯敏斯特教堂'], 'total': 7}]

    [{'item': ['威斯敏斯特教堂'], 'total': 7},
    {'item': ['威斯敏斯特教堂', '环球剧场'], 'total': 13},
    {'item': ['威斯敏斯特教堂', '环球剧场'], 'total': 13},
    {'item': ['威斯敏斯特教堂', '环球剧场'], 'total': 13}]

    [{'item': ['威斯敏斯特教堂'], 'total': 7},
    {'item': ['威斯敏斯特教堂', '环球剧场'], 'total': 13},
    {'item': ['威斯敏斯特教堂', '英国国家美术馆'], 'total': 16},
    {'item': ['威斯敏斯特教堂', '环球剧场', '英国国家美术馆'], 'total': 22}]

    [{'item': ['威斯敏斯特教堂'], 'total': 7},
    {'item': ['威斯敏斯特教堂', '环球剧场'], 'total': 13},
    {'item': ['威斯敏斯特教堂', '英国国家美术馆'], 'total': 16},
    {'item': ['威斯敏斯特教堂', '环球剧场', '英国国家美术馆'], 'total': 22}]

    [{'item': ['圣保罗大教堂'], 'total': 8},
    {'item': ['威斯敏斯特教堂', '圣保罗大教堂'], 'total': 15},
    {'item': ['威斯敏斯特教堂', '环球剧场', '圣保罗大教堂'], 'total': 21},
    {'item': ['威斯敏斯特教堂', '英国国家美术馆', '圣保罗大教堂'], 'total': 24}]
    """

    # 背包问题
    check_list = [
        item('音响', 4, 3000),
        item('笔记本电脑', 3, 2000),
        item('吉他', 1, 1500),
        item('iphone', 1, 2000)
    ]
    max_requirement = 4
    """
    [{'item': [], 'total': 0},
    {'item': [], 'total': 0},
    {'item': [], 'total': 0},
    {'item': ['音响'], 'total': 3000}]

    [{'item': [], 'total': 0},
    {'item': [], 'total': 0},
    {'item': ['笔记本电脑'], 'total': 2000},
    {'item': ['音响'], 'total': 3000}]

    [{'item': ['吉他'], 'total': 1500},
    {'item': ['吉他'], 'total': 1500},
    {'item': ['笔记本电脑'], 'total': 2000},
    {'item': ['笔记本电脑', '吉他'], 'total': 3500}]

    [{'item': ['iphone'], 'total': 2000},
    {'item': ['吉他', 'iphone'], 'total': 3500},
    {'item': ['吉他', 'iphone'], 'total': 3500},
    {'item': ['笔记本电脑', 'iphone'], 'total': 4000}]
    """

    # 野营问题
    check_list = [
        item('水', 3, 10),
        item('书', 1, 3),
        item('食物', 2, 9),
        item('夹克', 2, 5),
        item('相机', 1, 6),
    ]
    max_requirement = 6
    """
    [{'item': [], 'total': 0},
    {'item': [], 'total': 0},
    {'item': ['水'], 'total': 10},
    {'item': ['水'], 'total': 10},
    {'item': ['水'], 'total': 10},
    {'item': ['水'], 'total': 10}]

    [{'item': ['书'], 'total': 3},
    {'item': ['书'], 'total': 3},
    {'item': ['水'], 'total': 10},
    {'item': ['水', '书'], 'total': 13},
    {'item': ['水', '书'], 'total': 13},
    {'item': ['水', '书'], 'total': 13}]

    [{'item': ['书'], 'total': 3},
    {'item': ['食物'], 'total': 9},
    {'item': ['书', '食物'], 'total': 12},
    {'item': ['水', '书'], 'total': 13},
    {'item': ['水', '食物'], 'total': 19},
    {'item': ['水', '书', '食物'], 'total': 22}]

    [{'item': ['书'], 'total': 3},
    {'item': ['食物'], 'total': 9},
    {'item': ['书', '食物'], 'total': 12},
    {'item': ['食物', '夹克'], 'total': 14},
    {'item': ['水', '食物'], 'total': 19},
    {'item': ['水', '书', '食物'], 'total': 22}]

    [{'item': ['相机'], 'total': 6},
    {'item': ['书', '相机'], 'total': 9},
    {'item': ['食物', '相机'], 'total': 15},
    {'item': ['书', '食物', '相机'], 'total': 18},
    {'item': ['食物', '夹克', '相机'], 'total': 20},
    {'item': ['水', '食物', '相机'], 'total': 25}]
    """


    for i in main(check_list, max_requirement):
        pprint(i)
        print()
posted @ 2021-06-28 17:24  pywjh  阅读(98)  评论(0编辑  收藏  举报
回到顶部