3.动态规划

3.动态规划

3.1记忆搜索算法

对斐波那契数列进行改进,使用记忆功能来保存已经完成过的计算,当下次需要某个计算时,如果记忆列表中有计算结果,则直接使用。否则进行迭代。

from collections import defaultdict
from datetime import datetime

total = defaultdict(int)  # 存储已经计算好的值


def fib_test(k):
    # 递归求解第k个数的值
    assert k > 0, "k必须大于0"
    if k in [1, 2]:
        return 1
    global total
    if k in total:
        result = total[k]
    else:
        result = fib_test(k - 1) + fib_test(k - 2)
        total[k] = result
    return result


if __name__ == "__main__":
    # 搜索+记忆 算法
    start_time = datetime.now()
    print("斐波那契数列第50次的结果为:{}".format(fib_test(50)))
    print("循环耗时:{}".format((datetime.now() - start_time).total_seconds()))
    print(total)

运行结果:
在这里插入图片描述

3.2数字金字塔-回溯法

1)多阶段决策

根据过程的特性可以将过程按空间、时间等标志分为若干个互相联系又互相区别的阶段。
在每一个阶段都需要做出决策,从而使整个过程达到最好的效果。
各个阶段决策的选取不是任意确定的,它依赖于当前面临的状态,又影响以后的发展。
当各个阶段的决策确定后,就组成了一个决策序列,因而也就确定了整个过程的一条活动路线,这样的一个前后关联具有链状结构的多阶段过程就称为多阶段决策问题。

2)动态规划

对最佳路径(最佳决策过程)所经过的各个阶段,其中每个阶段始点到全过程终点的路径,必定是该阶段始点到全过程终点的一切可能路径中的最佳路径(最优决策),这就是美国数学家Bellman提出的著名的最优化原理。
简言之,一个最优策略的子策略必然也是最优的。
把多阶段决策问题转化为一系列单阶段最优问题,从而逐个求解。
动态规划方法的关键:在于正确地写出基本的递推关系式和恰当的边界条件(简称基本方程)。
要做到这一点,就必须将问题的过程分成几个相互联系的阶段,恰当的选取状态变量和决策变量及定义最优值函数,从而把一个大问题转化为一组同类型的子问题,然后逐个求解。
即从边界条件开始,逐段递推寻优,在每一个子问题的求解中,均利用了它前面的子问题的最优化结果,依次进行,最优一个子问题所得的最优解,就算整个问题的最优解。
动态规划的特点:
每个阶段的最优决策过程只与本阶段的初始状态有关,而与以前各阶段的决策无关(即为了到达本阶段的初始状态而采用哪组决策路线无关)。换言之,本阶段之前的状态与决策,只是通过系统在本阶段所处的初始状态来影响本阶段及以后各个阶段的决策。或者说,系统过程的历史只能通过系统现阶段的状态去影响系统的未来。具有这种性质的状态称为无后效性状态(马尔科夫性)。
动态规划方法只适用于求解具有无后效性状态的多阶段决策问题。

3)算法实现

数字金字塔旨在找到最大的数。从上往下,每层找一个。

# 数字金字塔-回溯法
pyramid = [
    [13],
    [11, 8],
    [12, 7, 26],
    [6, 14, 15, 8],
    [12, 17, 13, 34, 11]
]

datas = [13]
total_path = []


def search(depth, y):
    if depth == 5:
        total_path.append(datas[:])
        return
        # 1. 选择下方的值
    # 1.设置现场
    datas.append(pyramid[depth][y])
    # 2.递归
    search(depth + 1, y)
    # 3.恢复现场
    datas.pop()
    # 2.选择右下方的值
    # 1.设置现场
    datas.append(pyramid[depth][y + 1])
    # 2.递归
    search(depth + 1, y + 1)
    # 3.恢复现场
    datas.pop()


if __name__ == "__main__":
    search(1, 0)
    max = 0
    max_pos = 0
    for index, data in enumerate(total_path):
        if sum(data) > max:
            max = sum(data)
            max_pos = index
    print(max_pos)
    print(total_path[max_pos])

运行结果:
在这里插入图片描述

3.3数字金字塔-记忆式搜索

# 数字金字塔-记忆搜索
pyramid = [
    [13],
    [11, 8],
    [12, 7, 26],
    [6, 14, 15, 8],
    [12, 17, 13, 24, 11]
]

info = {}  # 存储每个节点下的最大长度


def search_max(depth, y):
    if depth == 4:
        return pyramid[depth][y]

    if "{}_{}".format(depth, y) in info:
        # 记忆搜索
        return info["{}_{}".format(depth, y)]
    else:
        # 1.把左下方的值交给下一个人得到最大值
        left_max = search_max(depth + 1, y)
        # 2.把右下方的值交给下一个人得到最大值
        # 1.任务可以下分。2.最优子结构。3.决策。
        right_max = search_max(depth + 1, y + 1)
        max_value = pyramid[depth][y] + max(left_max, right_max)
        info["{}_{}".format(depth, y)] = max_value
        return max_value


if __name__ == "__main__":
    print(search_max(0, 0))
    print("记忆结果:{}".format(info))

运行结果:
在这里插入图片描述

3.5投资分配

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.6投资分配算法实现

# 投资分配

# 利润表 投资多少万对应的利润,0,10,20,30,40,50,60
values = [
    [0, 20, 50, 65, 80, 85, 85],
    [0, 20, 40, 50, 55, 60, 65],
    [0, 25, 60, 85, 100, 110, 115],
    [0, 25, 40, 50, 60, 65, 70]
]

# 分析过程

# # 老三统计好各种情况
# pre_max = [0]  # 老三的利润总表
# for i in range(10, 61, 10):
#     # i 代表老三假设手中有i万元
#     value_list = []  # 老三的利润分表
#     for j in range(0, i + 1, 10):
#         value_list.append(values[3][int(j / 10)] + values[2][int((i - j) / 10)])
#     pre_max.append(max(value_list))
# print(pre_max)
#
# # 老二统计好各种情况
# pre_max2 = [0]
# for i in range(10, 61, 10):
#     # i 代表老二假设手中有i万元
#     value_list = []
#     for j in range(0, i + 1, 10):
#         value_list.append(pre_max[int(j / 10)] + values[1][int((i - j) / 10)])
#     pre_max2.append(max(value_list))
# print(pre_max2)
#
# # 老大统计好各种情况
# pre_max3 = []
# for i in range(10, 61, 10):
#     # i 代表老大假设手中有i万元
#     value_list = []
#     for j in range(0, i + 1, 10):
#         value_list.append(pre_max2[int(j / 10)] + values[0][int((i - j) / 10)])
#     pre_max3.append(max(value_list))
# print(pre_max3)
# print(max(pre_max3))

# 动态规划过程

pre_max = values[3]
for k in range(len(values) - 1, -1, -1):
    new_pre_max = [0]
    for i in range(10, 61, 10):
        value_list = []
        for j in range(0, i + 1, 10):
            value_list.append(pre_max[int(j / 10)] + values[k-1][int((i - j) / 10)])
        new_pre_max.append(max(value_list))
    pre_max = new_pre_max

print(pre_max)
print("最大利润为 = {}".format(max(pre_max)))

运行结果:
在这里插入图片描述

3.7背包问题

在这里插入图片描述
在这里插入图片描述

3.8 背包问题-回溯法

# 通过回溯法求解0-1背包问题
# [重量,价值]
info = [
    [3, 8],
    [2, 5],
    [5, 12]
]

selects = []

max_selects = []
max_value = 0


def search(depth, rest):
    if depth == 3:
        print(selects)
        values = []
        for index, select in enumerate(selects):
            values.append(select * info[index][1])
        global max_value
        if sum(values) > max_value:
            max_value = sum(values)
            global max_selects
            max_selects = selects[:]
    else:
        # 1.不放
        # 1.设置现场
        selects.append(0)
        # 2.递归
        search(depth + 1, rest)
        # 3.恢复现场
        selects.pop()

        # 2.放
        if rest >= info[depth][0]:
            # 1.设置现场
            selects.append(1)
            # 2.递归
            search(depth + 1, rest - info[depth][0])
            # 3.恢复现场
            selects.pop()


if __name__ == "__main__":
    search(0, 5)
    print("------------------")
    print(max_selects)
    print(max_value)

运行结果:

在这里插入图片描述

3.9背包问题-记忆搜索

# 通过记忆搜索的回溯法求解0-1背包问题
# [重量,价值]
info = [
    [3, 8],
    [2, 5],
    [5, 12]
]

mem = {}  # 存储计算结果


def search(depth, rest):
    # 放还是不放 决策
    if "{}_{}".format(depth, rest) in mem:
        return mem["{}_{}".format(depth, rest)]

    if depth == 2:
        data = info[depth][1] if rest >= info[depth][0] else 0
    else:
        values = []
        # 1.分任务
        values.append(search(depth + 1, rest))
        if rest >= info[depth][0]:
            values.append(search(depth + 1, rest - info[depth][0]) + info[depth][1])
        # 2.选最大(决策)
        data = max(values)
    if "{}_{}".format(depth, rest) not in mem:
        mem["{}_{}".format(depth, rest)] = data
    return data


if __name__ == "__main__":
    print(search(0, 5))
    print("------------------")
    print(mem)

运行结果:
在这里插入图片描述

3.10背包问题-动态规划

# 通过动态规划求解0-1背包问题
# [重量,价值]
info = [
    [3, 8],
    [2, 5],
    [5, 12]
]

# 分析
# 老三初始化
# pre_max = [0]
# total = 5
# for i in range(1, info[-1][0]):
#     pre_max.append(0)
# for i in range(info[-1][0], total + 1):
#     pre_max.append(info[-1][1])
#
# print(pre_max)

# # 老二
# new_pre_max = [0]
# for i in range(1, total + 1):
#     value_list = []
#     for j in range(0, i + 1):
#         # j代表留给自己
#         if j >= info[1][0]:
#             value_list.append(info[1][1] + pre_max[i - j])
#         else:
#             value_list.append(pre_max[i - j])
#     new_pre_max.append(max(value_list))
#
# print(new_pre_max)
#
# new_pre_max2 = [0]
# # 老大
# for i in range(total, total + 1):
#     value_list = []
#     for j in range(0, i + 1):
#         # j代表留给自己
#         if j >= info[0][0]:
#             value_list.append(info[0][1] + new_pre_max[i - j])
#         else:
#             value_list.append(new_pre_max[i - j])
#     new_pre_max2.append(max(value_list))

# print(new_pre_max)


# 动态规划
# 老三初始化
pre_max = [0]
total = 5
for i in range(1, info[-1][0]):
    pre_max.append(0)
for i in range(info[-1][0], total + 1):
    pre_max.append(info[-1][1])

for k in range(len(info) - 1, -1, -1):
    new_pre_max = [0]
    start = 1
    # if k == 0:
    #     start = total
    for i in range(start, total + 1):
        value_list = []
        if i >= info[k - 1][0]:
            value_list.append(info[k - 1][1] + pre_max[i - info[k - 1][0]])
        value_list.append(pre_max[i])
        new_pre_max.append(max(value_list))
    pre_max = new_pre_max
print(new_pre_max)

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值