数据结构+算法(第06篇):再不会“降维打击”你就Out了!

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析

阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

 

引言

刘慈欣的《三体》不仅让中国的硬科幻登上了世界的舞台,更是给广大读者普及了诸如“降维打击”之类的热门概念。

“降维打击”之所以给人如此之震撼,在于它以极简的方式,从更高的、全新的技术视角有效解决了当前困局。

那么在算法的世界中,是否存在这样的利器呢?答案是肯定的——那就是大名鼎鼎的“递归”。

递归思想与传统算法思想的区别

传统算法思想是正向演绎逻辑,即:根据已知条件,进行联想、寻找经验库,逐步推导,直到问题解决。

而递归思想是逆向归纳逻辑,即:当前问题的求解是否可以由规模小一点的问题求解叠加而来,后者是否可以再由更小一点的问题求解叠加而来……依此类推,直到收敛为一个极简的出口问题的求解。

由上可知,递归的本质借鉴的就是数学归纳法:
证明一个与自然数n有关的命题S(n),若:
(1)可证明当n取第一个值n1时命题成立;
(2)假设当n=kk>=n1,k为自然数)时命题成立,可证明当n=k+1时命题也成立。

那么综合(1)(2),对一切自然数n(n>=n1),命题S(n)都成立。

把上面数学归纳法定义中的“证明”换成“求解”,就是递归思想:)

数学归纳法的一个直观理解就是“多米诺骨牌”:

只要推倒第一张骨牌(解决出口问题),那么后面的骨牌(规模逐次升高的问题)就依次倒下(被解决)。

递归思想的优势

说了半天递归思想的意义,那么它到底在实操时有什么优势呢?

由上图可知:

  1. 传统算法是以“步骤”为中心的、递归算法是以“状态/规模”为中心的;

  2. 传统算法必须有步骤序列的全景图,才能解决问题;递归算法只要有“状态转移函数”和初始状态/出口问题的解,就能解决问题;

  3. 针对当前问题,如果很难直接推导出完整的步骤序列,那么传统算法是很难凑效的。

换言之,递归算法将面向过程的求解思路转换成面向状态机的求解思路。

而计算机本质就是一个状态机,这个天然的性质决定了递归算法更适合计算机“理解”、求解。

递归应用的初步套路

第一步:识别规模因子

第二步:识别初始状态和出口问题,求对应的解

第三步:识别状态转移条件、抽象状态转移函数

现有一个级数为n的台阶,每次你可以爬1,2或者3级台阶,请问爬完整个n级台阶有多少种方法?

解答:

第一步:识别规模因子。

规模因子说白了就是个变量,首先在题目中找变量。

显然,题目中变量由两个:一个是台阶的级数,一个是每次爬的台阶数

那么哪个变量是规模因子呢?

判定规模因子的方法:变量的取值范围是否是未知的、是否范围是很大的?

每次爬的台阶数的取值范围是1,2,3,是确定的;台阶的级数的取值范围是不确定的。

所以规模因子是台阶的级数。

第二步:识别初始状态和出口问题,求对应的解。

初始状态就是规模因子缩小到最小时的状态。

出口问题包含初始状态对应的问题和边界问题。

在本题中,显然规模因子最小的状态就是台阶的级数为1的状态,此时显然种数为1(爬1级到到第1级台阶)

边界问题指的是当规模因子非正常取值的时候的问题形态。

在本题中,显然台阶数如果取0或者负值,是非正常取值,这个时候的问题形态就是边界问题。

显然在这样的场景下,是无解的,即:方法的种数是0

第三步:识别状态转移条件、抽象状态转移函数。

按照上面递归思想的图示,求状态转移函数,就是构造规模n-1的解映射到规模n的解的函数。

针对本题:

规模n-1就对应n-1级台阶;规模n-1的解就是爬到第n-1级台阶的方法种数。

规模n就对应n级台阶;规模n的解就是爬到第n级台阶的方法种数。

而从第n-1级台阶爬到第n级台阶,就只有一种方法,向上爬1步。根据排列组合的乘法原理:

f(n)=f(n-1) x 1,即:f(n)=f(n-1)

看上去,我们已经按照套路“完美”解决了这个问题。但真的是这样吗?

细心的你,肯定发现了:

由于每次爬台阶可以爬1,2或者3步,但是上面的转移函数只考虑了爬1步的情况!

很容易看出,问题的根因来自上面递归思想的图示!

其实,上面的递归思想的图示只描述了递归思想的核心,是个简化版;

更一般的正确形式是下图所示:

稍稍比较一下两张图就可以看出:

区别在于降维时的状态单一性。

在简化版图示中,降维前后的状态是单一的;而在一般版图示中,降维前后是多个状态的组合!

回到本题,如果应用上面的一般版图示,那么降维前的状态就是台阶数为n的情况,降维后的状态就是台阶数分别为n-1, n-2n-3这3种情况的组合。

1. f(n-1) -> f(n):

从第n-1级台阶到第n级台阶只有1种爬法

根据排列组合的乘法原理 =>f(n)=f(n-1)x1 (式1)

2. f(n-2) -> f(n):

从第n-2级台阶到第n级台阶有2种爬法:

第1种:直接爬2级到第n级

根据排列组合的乘法原理 =>此时f(n)=f(n-2) x 1

第2种:先爬1级,变成f(n-1)的情况,再叠加f(n-1)的解

显然该情况包含在f(n-1)的所有情况中,忽略、不做重复计算

所以根据排列组合的加法原理,整体:
f>f(n)=f(n-2)x1 (式2)

3. f(n-3) -> f(n):

从第n-3级台阶到第n级台阶有3种爬法:

第1种:直接爬3级到第n级

根据排列组合的乘法原理 =>此时f(n)=f(n-3) x 1

第2种:先爬1级,变成f(n-2)的情况,再叠加f(n-2)的解

同上,显然该情况包含在f(n-2)的所有情况中,忽略、不做重复计算

第2种:先爬2级,变成f(n-1)的情况,再叠加f(n-1)的解

同上,显然该情况包含在f(n-1)的所有情况中,忽略、不做重复计算

f()中的变量的正常取值>=1,从而有n-3>=1 => n>=4

且根据排列组合的加法原理 =>f(n)=f(n-3)x1(式3)

根据排列组合的加法原理、综合式1、式2、式3,得到:
n>=4时:
f(n)=f(n-1)+f(n-2)+f(n-3) (式4)

我们在第一步时,只求了初始状态n=1的解:
f(1)=1 (式5)

根据(式4)和上面的一般版图示,我们还得求n=2,3时的解。

对于n=2有以下情况:

  1. 初始状态问题转移:f(1) -> f(2):

从第1级台阶到第2级台阶只有1种爬法

根据排列组合的乘法原理 =>f(2)=f(1)x1 (式6)

  1. 边界问题转移:f(0) -> f(2):

从第0级台阶到第2级台阶有2种爬法:

第1种:直接爬2级到第2级

注意:对于边界问题,不能直接应用排列组合的乘法原理,否则恒等于0!

此时要省去边界解作为乘法因子!

换言之,对于当前这种情况,不能写成f(2) = f(0) x 1,要写成

f(2) = 1

第2种:先爬1级,变成f(1)的情况,再叠加f(1)的解

显然该情况包含在f(1)的所有情况中,忽略、不做重复计算

所以根据排列组合的加法原理,整体:f(2)=f(1)x1+1=1x1+1=2 (式8)

同理,可求得:f(3)=3 (式9)

综合式4、式5、式8、式9,得到最终的递归算法:>当n>=4时:

    f(n)=f(n-1)+f(n-2)+f(n-3)
    当1<=n<4时:f(n)=n
    当n<1时:f(n)=0

递归应用的优化套路

从上面的例子可以看出,在实操中使用“递归应用的初步套路”可能会遇到降维后状态非单一问题,该问题会导致必须扩展初始状态/边界问题的集合。

扩展的集合的大小取决于通用状态转移函数的定义域与初始状态之差。

上面的例子中,通用状态转移函数的定义域是n>=4,初始状态是n=1,所以扩展范围是n=2和n=3。

由此,我们可以得到递归应用的优化套路:

第一步:识别规模因子

第二步:识别状态转移条件、抽象状态转移函数

第三步:识别初始状态和边界问题、求函数值

第四步:根据第二步和第三步求出扩展集合

第五步:对扩展集合中的元素逐一求函数值

第六步:综合第二步、第三步、第五步的结果

进一步思考的话,可以看出:

扩展集合实际上对应的就是通用状态转移函数失效的取值范围。

状态转移函数降级的话,就可以缩小扩展集合的大小。不断降级,就可以不断逼近初始状态。

上面的例子中,f(2) = f(1) x 1 + 1就是f(n) = f(n-1),它实际上就是

f(n) = f(n-1) + f(n-2) + f(n-3)

的降级形式——f(n-2)f(n-3)去掉就转换成了f(n) = f(n-1)——其失效的取值范围就是n=2n=3

递归算法的局限性

从递归算法思想一般版图示可以看出:

局限性1(适用性问题):

如果“降维”前的状态集合并不方便用“降维”后的状态集合表示,即状态转移函数不好求,那么该场景使用递归不一定恰当。

局限性2(重复计算问题):

在直接递归的过程中部分函数值会被重复计算。

那么如何解决上面两个问题呢?答案就是“动态编程”。

下篇文章我们就来详细谈谈“动态编程”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/363834.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

各类型判空操作

开发中经常遇到需要判空的地方&#xff0c;比如对字符串进行判空操作。 而有时候工具包太多不知道用哪个。 就像下图&#xff0c;光一个 StringUtils 就有十几个包弹出来。 怎么选&#xff1f; 其实用哪个都行。 最重要的是&#xff1a; 有一套自己用的顺手的工具。 或者…

Java基础数据结构之ArrayList源码分析

一.几个常量 这是默认容量 这两个是共享的空对象 这是真正存储元素的地方&#xff0c;现在还没有分配内存 二.构造方法 这是一个无参构造方法&#xff0c;此时让存储元素的数组指向了那个默认容量数组&#xff0c;此时该数组是一个空数组&#xff0c;长度为0. 这是给定初始容量…

C#使用OpenCvSharp4库读取电脑摄像头数据并实时显示

一、OpenCvSharp4库 OpenCvSharp4库是一个基于.Net封装的OpenCV库&#xff0c;Github源代码地址为&#xff1a;https://github.com/shimat/opencvsharp&#xff0c;里面有关于Windows下安装OpenCvSharp4库的描述&#xff0c;如下图所示&#xff1a; 二、C#使用OpenCvSharp4库…

杂题——试题-算法训练-P0604-runaround数

分析&#xff1a; 题目有三个关键点&#xff1a; 一&#xff1a;结束时&#xff0c;回到起始位置&#xff08;比较结束时和起始时的下标位置是否相同&#xff09;二&#xff1a;该整数的所有数字都必须遍历一遍&#xff0c;且只能遍历一遍&#xff08;把遍历过的数字做个标记&a…

【前端-VUE+TS】Vue3组件化-知识补充(六)

一. 动态组件 比如我们现在想要实现了一个功能&#xff1a; 点击一个tab-bar&#xff0c;切换不同的组件显示&#xff1b; 案例截图 这个案例我们可以通过两种不同的实现思路来实现&#xff1a; 方式一&#xff1a;通过v-if来判断&#xff0c;显示不同的组件&#xff1b;方式二…

周润发节俭,排队买廉价盒饭,身价56亿由此省出。

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 周润发裸捐数十亿&#xff0c;但生活中的他却极度节俭。每一分…

基于SSM的高校社团管理系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 我欲乘风归去 又恐琼楼玉宇 高处不胜寒 -苏轼 目录 一、项目简介 二、开发技术与环境配置 2.1 SSM框架 2.2 …

UGUI中Text和TextMeshPro实现图文混排方式

一些项目中实现图文混排是自定义一个脚本去继承Text类&#xff0c;然后文本中用富文本的方式进行图片和超链接的定义&#xff0c;在代码中用正则表达式匹配的方式把文本中图片和超链接给替换&#xff0c;如下&#xff1a; TextMeshPro实现是生成SpriteAsset进行图文混排的&…

杂题——试题 算法训练 试题3971 丑数

分析&#xff1a; 判断一个数 n 是否是丑数&#xff0c;分成三个部分 1、寻找因数&#xff0c;从2遍历到 n&#xff0c;如果该数 i 是 n 的因数&#xff0c;就进入下一步2、判断 i 是否是质数&#xff0c;这部分代码直接套用即可&#xff0c;见得较多3、最后判断 i 是否等于2或…

2024 springboot Mybatis-flex 打包出错

Mybatis-flex官网&#xff1a;快速开始 - MyBatis-Flex 官方网站 从 Mybatis-flex官网获取模板后&#xff0c;加入自己的项目内容想打包确保错&#xff0c;先试试一下方法 这里改成skip的默认是true改成false&#xff0c;再次打包就可以了

ATFX汇市:鲍威尔否认3月降息,晚间英央行决议大概率按兵不动

ATFX汇市&#xff1a;今日3:00&#xff0c;美联储公布利率决议结果&#xff0c;维持5.5%的联邦基金利率不变&#xff0c;美元指数五分钟内上涨0.19%&#xff0c;最高触及103.51点。半小时后的3:30&#xff0c;美联储主席鲍威尔开始讲话&#xff0c;美元指数分钟级别剧烈下跌&am…

【服务端性能测试】测试方案设计(实操需要准备的内容)

一般性能测试流程都是&#xff1a;获取测试需求——>测试需求分析——>测试方案设计——>压测环境搭建&#xff08;目前是线上&#xff09;——>测试数据准备——>测试脚本准备、调试——>测试脚本执行——>监控数据录入——>测试结果跟开发一起分析—…

设计模式——模板方法模式(Template Method Pattern)

概述 模板方法模式&#xff1a;定义一个操作中算法的框架&#xff0c;而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法模式是一种基于继承的代码复用技术&#xff0c;它是一种类行为型模式。模板方法模式是结…

TCP 协议的相关特性

1. TCP格式 TCP特性&#xff1a;有连接&#xff0c;全双关&#xff0c;面向字节流&#xff0c;可靠传输。&#xff08;TCP安身立命的本钱&#xff0c;初心就是解决“可靠传输”问题&#xff09; 其实TCP的特征有很多这里我就简单的介绍几个。 2. 确认应答 其实用来确保可靠性&…

【新课】安装部署系列Ⅲ—Oracle 19c Data Guard部署之两节点RAC部署实战

本课程由云贝教育-刘峰老师出品&#xff0c;感谢关注 课程介绍 Oracle Real Application Clusters (RAC) 是一种跨多个节点分布数据库的企业级解决方案。它使组织能够通过实现容错和负载平衡来提高可用性和可扩展性&#xff0c;同时提高性能。本课程基于当前主流版本Oracle 1…

2024.1.30 GNSS 学习笔记

站星双差Kalman滤波伪距差分定位流程 1. RTK定位技术&#xff08;实时载波相位差分技术&#xff09;原理-站间单差浮点解 1.RTK技术其实就是在RTD技术的基础上增加载波观测值的使用。由于伪距的噪声在分米量级&#xff0c;即使我们通过站间单差消除了绝大部分的误差影响&…

前端入门第二天

目录 一、列表、表格、表单 二、列表&#xff08;布局内容排列整齐的区域&#xff09; 1.无序列表&#xff08;不规定顺序&#xff09; 2.有序列表&#xff08;规定顺序&#xff09; 3.定义列表&#xff08;一个标题多个分类&#xff09; 三、表格 1.表格结构标签 2.合并…

基于Raspberry Pi的自动巡航与避障系统(二)

在上一篇中&#xff0c;我们讨论了智能小车的避障逻辑实现&#xff0c;在本篇中&#xff0c;我们将进一步扩展智能小车的功能&#xff0c;包括更高级的避障策略、路径规划和导航功能&#xff0c;同时&#xff0c;我们还将提供相应的代码示例&#xff0c;以帮助读者更好地理解和…

Typora导出html文件图片自动转换成base64

Typora导出html文件图片自动转换成base64 一、出现问题二、解决方案三、编码实现3.1.创建Java项目3.2.代码3.3.打包成Jar包 四、如何使用endl 一、出现问题 typora 导出 html 的时候必须带有原图片&#xff0c;不方便交流学习&#xff0c;文件太多显得冗余&#xff0c;只有将图…

GNSS技术助力航海业迈向新时代:海洋测绘与航行的创新应用

全球导航卫星系统&#xff08;GNSS&#xff09;技术在海洋测绘与航行领域的广泛应用&#xff0c;正推动航海业迎来新一轮的科技变革。MinewSemi的GNSS模块为船舶导航、海洋资源勘探和航行安全提供了更为精确和高效的解决方案。本文将深入研究GNSS技术在海洋测绘与航行中的创新应…