运筹学-动态规划之数字组合 关于bestSum()方法问题的修改

本文内容为2022-2023学年第一学期运筹学课程动态规划部分的程序修改与讲解。


在课堂上展示的程序中,运行后可能会出现这样的结果:

memo = {}

def bestSum(targetSum, numbers):
    shortest = None
    ## 终端条件
    if targetSum == 0:
        return []
    if targetSum < 0:
        return None
    if memo.get(targetSum) is not None:
        return memo.get(targetSum)

    ## 递归
    ## 状态转移
    for number in numbers:
        reminder = targetSum - number
        reminder_r = bestSum(reminder,numbers)

        if reminder_r is not None:
            reminder_r.append(number)
            if shortest is None or len(reminder_r) < len(shortest):
                shortest = reminder_r.copy()
    memo[targetSum] = shortest
    return shortest

print(bestSum(100, [5, 4, 25]))

 可以看出,结果出现了明显的错误,对于这一特定案例,可以通过将大的数放在前面的策略进行规避:

print(bestSum(100, [25, 5, 4]))

输出结果:

 现在的结果看似正确,但是打印一下memo记录,发现还是存在问题的:

print(memo)

由于表格过长,这里就不一一列举,可以发现,除了最终target 100的memo结果正确之外,其他的记录都或多或少存在一些问题,因此原来的程序中一定存在一些没有发现的错误。

在讲解程序之前,先提一下关于python的对象.copy()方法的有关知识:

# 首先定义一个list对象object1(这里任何python中的对象均可)
object1 = [1, 2, 3, 4]
# 使用id()方法查看对象的标识,可以理解为对象的地址
# id标识在对象的生命周期内是唯一且恒定的
print("id of object1", id(object1))
# 再定义一个对象object2,直接用object1为其赋值
object2 = object1
# 打印object2的标识,可以看到二者的标识是相同的,意味着object1
# 和object2这两个变量名指向的是用一个对象
print("id of object2", id(object2))
# 再使用.copy()方法对object3进行赋值
object3 = object1.copy()
# 打印object3的标识,可以看到object3与object1的标识并不相同,
# 这表示二者指向不同的对象
print("id of object3", id(object3))

上述程序的一组输出结果如下(大家实际运行时输出的id具体值可能会不同,但关系是相同的):

了解了这些前置知识后,就可以开始讲解程序中存在的问题了。

我们首先关注一下程序中使用的几个主要的对象(在本题中为列表list对象):

 而在程序中,包含了以下几次对象赋值与修改(append)的操作:

 

可以发现,仅在给shortest对象赋值时,采用了.copy()方法,将reminder_r的值复制到了新的对象中,而其他几处都是直接采取了引用对象的赋值方法,因此在之后对reminder_r进行append操作时,实质上会出现直接对memo中记录的路径对象进行修改的情况:

因此,为了防止这样的操作对memo进行不正当修改,应在每一次赋值与返回值时,都采用.copy()方法。最终修改后的程序如下:

memo = {}

def bestSum(targetSum, numbers):
    shortest = None
    ## 终端条件
    if targetSum == 0:
        return []
    if targetSum < 0:
        return None
    if memo.get(targetSum) is not None:
        return memo.get(targetSum).copy()  # 值传递

    ## 递归
    ## 状态转移
    for number in numbers:
        reminder = targetSum - number
        reminder_r = bestSum(reminder,numbers)

        if reminder_r is not None:
            reminder_r.append(number)
            if shortest is None or len(reminder_r) < len(shortest):
                shortest = reminder_r.copy()    # 值传递
    if shortest is not None:    # 加上判断,消除memo中的无效记录None
        memo[targetSum] = shortest.copy()
    return shortest

print(bestSum(100, [25, 5, 4]))

输出结果:

 memo记录(部分):

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值