热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

算法学习递归算法

文章目录应用条件案例分析问题描述解法分享解法改进递归次数的计算应用条件本博客在学习北京大学陈斌老师《数据结构与算法》MOOC课程中总结反思形成。递归是一种一种优雅的理解问题的方

文章目录

    • 应用条件
    • 案例分析
      • 问题描述
      • 解法分享
      • 解法改进
    • 递归次数的计算


应用条件

本博客在学习北京大学陈斌老师《数据结构与算法》MOOC课程中总结反思形成。

递归是一种一种优雅的理解问题的方式,也是我曾经比较学习过程中比较困惑的一个地方。

理解递归,不妨牢记递归应用的三要素:

  1. 基本结束条件

  2. 缩小问题规模

  3. 调用自身

递归的优点和缺点也及其显著,优点:肯定能够寻找到最优解;缺点:低效(重复计算太多)

案例分析

本博客分析一个经典的应用场景“找零问题”,分别给出自己的解法和陈斌老师的解法,以帮助分析和理解递归

问题描述

某货币体系中找零兑换的最少货币数问题

解法分享

分析问题,牢牢从三要素出发。

基本结束条件:找零正好是货币体系中的一张面额,返回1;

缩小问题规模和调用自身:找零减去某张面额后,求兑换硬币最少数量(递归调用自身);

本人解法如下:

def OddChange(valuelist, change):if change in valuelist:return 1elif change >= 25:return min(1 + OddChange(valuelist, change - 1), 1 + OddChange(valuelist, change - 5),1 + OddChange(valuelist, change - 10), 1 + OddChange(valuelist, change - 25))elif change >= 10:return min(1 + OddChange(valuelist, change - 1), 1 + OddChange(valuelist, change - 5),1 + OddChange(valuelist, change - 10))elif change >= 5:return min(1 + OddChange(valuelist, change - 1), 1 + OddChange(valuelist, change - 5))else:return 1 + OddChange(valuelist, change - 1)

陈斌老师解法如下:

def recMC(coinValueList, change):minCoins &#61; change# 最小规模&#xff0c;直接返回if change in coinValueList:return 1else:for i in [c for c in coinValueList if c <&#61; change]:# 减小规模&#xff0c;调用自身numCoins &#61; 1 &#43; recMC(coinValueList, change - i)if numCoins

使用timeit分析时间开销如下&#xff1a;

image-20210918234817370

  • 可以很明显发现两者解法的时间开销是指数增长的速度&#xff0c;这印证了前文**低效&#xff08;重复计算太多&#xff09;**的观点
  • 老师的写法更高雅&#xff0c;可以学习&#xff0c;但是for 循环一定程度上降低了速度
  • 以求解63找零为例&#xff0c;总的递归次数我计算出来是67716925次数

解法改进

这部分我觉得是让我升华了的地方&#xff0c;前面的时间开销让我觉得递归算法是一个非常不靠谱的算法&#xff0c;毕竟一个63的找零就需要花费30s&#43;&#xff0c;这种开销难以承受。但是&#xff0c;在“空间换时间”思想的知道下&#xff0c;这个算法瞬间可以求解。

在递归调用过程中已经得到的最优解被记录下来。在递归调用之前&#xff0c;先查找表中是否已有部分找零的最优解。如果有&#xff0c;直接返回最优解而不进行递归调用&#xff1b;如果没有&#xff0c;才进行递归调用。

在实现方面&#xff0c;我的第一反应是使用词典进行保存&#xff1a;

def OddChangeOp(valuelist, change,resultdic):if change in valuelist:resultdic[str(change)] &#61; 1return 1elif str(change) in resultdic:return resultdic[str(change)]elif change >&#61; 25:resultdic[str(change)] &#61; min(1 &#43; OddChangeOp(valuelist, change - 1,resultdic), 1 &#43; OddChangeOp(valuelist, change - 5,resultdic),1 &#43; OddChangeOp(valuelist, change - 10,resultdic), 1 &#43; OddChangeOp(valuelist, change - 25,resultdic))return resultdic[str(change)]elif change >&#61; 10:resultdic[str(change)] &#61; min(1 &#43; OddChangeOp(valuelist, change - 1,resultdic), 1 &#43; OddChangeOp(valuelist, change - 5,resultdic),1 &#43; OddChangeOp(valuelist, change - 10,resultdic))return resultdic[str(change)]elif change >&#61; 5:resultdic[str(change)] &#61; min(1 &#43; OddChangeOp(valuelist, change - 1,resultdic), 1 &#43; OddChangeOp(valuelist, change - 5,resultdic))return resultdic[str(change)]else:resultdic[str(change)] &#61; 1 &#43; OddChangeOp(valuelist, change - 1,resultdic)return resultdic[str(change)]

但是&#xff0c;老师的解法更加有趣&#xff0c;直接使用列表完成了同样的工作&#xff0c;我使用词典的目的是希望构建映射关系&#xff0c;但其实列表本身也是有映射关系的&#xff0c;那就是列表的下标和列表的值

def recDC(coinValuelist, change, knowResults):minCoins &#61; change# 递归基本结束条件&#xff0c;记录最优解if change in coinValuelist:knowResults[change] &#61; 1return 1# 查表成功&#xff0c;直接使用最优解elif knowResults[change] > 0:return knowResults[change]else:for i in [c for c in coinValuelist if c <&#61; change]:numCoins &#61; 1 &#43; recDC(coinValuelist, change - i, knowResults)if numCoins

对比时间开销&#xff0c;可以发现&#xff0c;解法几乎是瞬间完成的&#xff0c;下图展示的是调用10000次显示的耗时&#xff0c;而且老师直接使用列表构造映射存储最优解的方案耗时比我的解法少的多。

同时&#xff0c;算法近似成为线性的时间的开销&#xff0c;内存开销也不算特别高&#xff0c;真正意义的可用算法&#xff0c;真的是非常神奇&#xff01;&#xff01;

image-20210919000113513

递归次数的计算

最后&#xff0c;分享一个有趣的事情&#xff0c;就是求解递归算法的迭代次数&#xff0c;这个在实现上有三种方法可以选择

  • 全局变量

  • 函数内置属性

  • 装饰器

其中&#xff0c;装饰器的方法简直太优雅了&#xff0c;这当然也是因为我本人对于python高阶语法的欠缺

这里mark记录一下

class Counter(object) :def __init__(self, fun) :self._fun &#61; funself.counter&#61;0def __call__(self,*args, **kwargs) :self.counter &#43;&#61; 1return self._fun(*args, **kwargs)&#64;Counter
def OddChange(valuelist, change):

写在最后&#xff0c;迭代算法在引入了储存机制后&#xff0c;我瞬间感觉简直无敌&#xff01;算法的魅力无穷&#xff0c;绝不是简单看别人轮子所能体会的。


推荐阅读
  • 深入解析Redis内存对象模型
    本文详细介绍了Redis内存对象模型的关键知识点,包括内存统计、内存分配、数据存储细节及优化策略。通过实际案例和专业分析,帮助读者全面理解Redis内存管理机制。 ... [详细]
  • 对象自省自省在计算机编程领域里,是指在运行时判断一个对象的类型和能力。dir能够返回一个列表,列举了一个对象所拥有的属性和方法。my_list[ ... [详细]
  • 反向投影技术主要用于在大型输入图像中定位特定的小型模板图像。通过直方图对比,它能够识别出最匹配的区域或点,从而确定模板图像在输入图像中的位置。 ... [详细]
  • 本文介绍如何使用 Python 的 xlrd 库读取 Excel 文件,并将其数据处理后存储到数据库中。通过实际案例,详细讲解了文件路径、合并单元格处理等常见问题。 ... [详细]
  • 本文详细探讨了 Django 的 ORM(对象关系映射)机制,重点介绍了其如何通过 Python 元类技术实现数据库表与 Python 类的映射。此外,文章还分析了 Django 中各种字段类型的继承结构及其与数据库数据类型的对应关系。 ... [详细]
  • 本文介绍如何在SQL Server中对Name列进行排序,使特定值(如Default Deliverable Submission Notification)显示在结果集的顶部。 ... [详细]
  • 开发笔记:9.八大排序
    开发笔记:9.八大排序 ... [详细]
  • 本文详细介绍了Python中列表的创建、访问、修改、排序及遍历等基本操作,帮助初学者快速掌握列表这一重要数据结构。 ... [详细]
  • 本文详细介绍了 iBatis.NET 中的 Iterate 元素,它用于遍历集合并重复生成每个项目的主体内容。通过该元素,可以实现类似于 foreach 的功能,尽管 iBatis.NET 并未直接提供 foreach 标签。 ... [详细]
  • Hybrid 应用的后台接口与管理界面优化
    本文探讨了如何通过优化 Hybrid 应用的后台接口和管理界面,提升用户体验。特别是在首次加载 H5 页面时,为了减少用户等待时间和流量消耗,介绍了离线资源包的管理和分发机制。 ... [详细]
  • 解决Python中 'NoneType' 对象无属性 'find_all' 错误
    本文详细探讨了在Python编程中遇到的常见错误——'NoneType'对象没有属性'find_all',并深入分析其原因及解决方案。通过理解find_all函数的工作原理和常见用法,帮助读者避免类似问题。 ... [详细]
  • Python第三方库安装的多种途径及注意事项
    本文详细介绍了Python第三方库的几种常见安装方法,包括使用pip命令、集成开发环境(如Anaconda)以及手动文件安装,并提供了每种方法的具体操作步骤和适用场景。 ... [详细]
  • 本文介绍了如何利用Python编程语言实现类似Photoshop的图像对比度调整功能。通过详细的算法解析和代码示例,帮助读者理解和应用这一技术。 ... [详细]
  • 本文介绍了如何使用 Python 的 Matplotlib 和 Pandas 库进行数据可视化。通过示例代码展示了折线图、柱状图和水平柱状图的创建方法,并解释了图表参数设置的具体细节。 ... [详细]
  • 本文将介绍 PyCharm 的一些实用快捷操作,包括自定义词典、代码块选择、快速环绕代码以及运行 Python 和 Django 控制台的方法。 ... [详细]
author-avatar
骁炉
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有