算法分析中递推式的一般代数解法

2024-05-04 23:18

本文主要是介绍算法分析中递推式的一般代数解法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

貌似看不清楚:http://blog.codinglabs.org/articles/linear-algebra-for-recursion.html

算法分析中经常遇到需要求解递推式的情况,即将递推式改写为等价的封闭形式。例如汉诺塔问题的时间复杂度递推形式为 T(n)=2T(n1)+1(n1) ,可以解出封闭形式为 T(n)=2n1 (设初始状态 T(0)=0 )。

因为递推式求解的重要性,许多算法书籍对其有专门介绍。Donald Knuth在Concrete Mathematics一书中多个章节都涉及递推式求解方法。算法导论也在第四章中专门论述的这个主题。

在这些相关论述中,主要介绍了一些启发式方法,这些方法往往需要一些特殊的技巧和灵感才能完成。

而本文将论述一种纯代数式的方法,这种方法将求解递推式转化为求解一个多项式的根和求解一组线性方程组,这样就使得整个求解过程不依赖于太多技巧,因此具有更好的易用性。

本文首先会给出两个例子:如何使用纯代数方法求解斐波那契数列和汉诺塔递推式;然后会借助线性代数论述这种方法背后的数学意义,说明线性递推式与线性方程的内在联系以及这种解法的数学原理;最后将例子中的方法推广到一般情况。

示例

例1:斐波那契数列

斐波那契数列大家应该很熟悉了,这里不再赘述,直接进入问题。

问题

设斐波那契数列为由如下递推式定义的数列:

T(0)T(1)T(n)===01T(n2)+T(n1)(n2)

求解 T(n) 的封闭形式(也就是斐波那契数列的通项公式)。

求解

首先忽略初始条件,考虑递推式 T(n)=T(n2)+T(n1) 。可以对解的形式进行一个猜测 T(n)=qn (这个不是瞎猜的,实际上可以证明线性递推式都遵循这种形式)。那么,递推式可以重写为:

T(n)=T(n2)+T(n1)qn=qn2+qn1q2=1+qq2q1=0

这样问题被转化为一个一元二次方程的求根问题。利用求根公式可得:

q=1±52

因此得到递推式的一个通解:

T(n)=c1(1+52)n+c2(152)n

即其中 c1 c2 为任意实数。下一步要代入初始条件解出 c1 c2 。根据n为0和1时的初始条件,可得:

c1+c2c1(1+52)+c2(152)==01

解得 c1=15 c2=15 。因此最终解为:

T(n)=15((1+52)n(152)n)

例2:汉诺塔

汉诺塔的时间复杂度通常使用递归式定义,在这个例子中将使用代数方法求解其封闭形式。

问题

汉诺塔的时间复杂度为 T(n)=2T(n1)+1 ,求解其封闭形式。

求解

这里并不能直接使用例1中的方法,因为右边除了递推项外,还有一个非递推项1,用线性代数的语言说,这个线性递推式是非齐次的。

可以回想一下线性代数中求解非齐次方程组通解的方法:1)求解其齐次部分的通解。2)求其一个特解,将特解加到通解上即得非齐次方程组通解。

我们用类似的方法求解汉诺塔时间复杂度递推式。首先,忽略后面的1,则得到一个齐次线性递推式:

T(n)=2T(n1)

转化为多项式方程:

T(n)=2T(n1)qn=2qn1q=2

因为方程是一次多项式,我们直接得到了其解为2。因此齐次递推式的通解为 c2n ,其中c为任意常数。

然后我们需要求得 T(n)=2T(n1)+1 的一个特解,解是一个以n为变量的函数。我们可以先从常数试起,设特解为 T(n)=a ,带入得 a=2a+1 , 解得 a=1 。因此,原递推式的通解为:

T(n)=c2n1

最后我们求解常数c。

将初始条件 T(0)=0 带入,得 0=c1 ,因此 c=1 。代入原通解,得汉诺塔时间复杂度递推式的封闭形式为:

T(n)=2n1

数学原理

上面两个例子可能有些同学看的不是很明白,其中提到了一些线性代数术语。在这一章节中,我们分析上述解法的数学原理,看看递推式是如何与线性代数关联起来的。

线性递推式的一般化

斐波那契数列和汉诺塔递推式可以看成线性递推式的特例,下面给出线性递推式的一般定义:

我们将满足如下递推关系的递推式称为线性递推式:

T(n)=a1T(n1)+a2T(n2)++ak1T(nk+1)+akT(nk)+C(n)

其中 C(n) 是只与n有关系的一个函数。如果 C(n)=0 ,则称递推式为齐次此,否则称为非齐次的。齐次递推式一定有平凡解 T(n)=0

注意仅有递推式是不能求得 T(n) 的唯一解,因此递推关系式只能给出一个通解。只有当下列初始条件确定后,才有可能给出 T(n) 的唯一特解。

T(0)=b0T(1)=b1T(k1)=bk1

齐次递推式求解定理

下面先考虑齐次线性递推式的求解。定理如下:

设有线性齐次递推式 T(n)=a1T(n1)+a2T(n2)++ak1T(nk+1)+akT(nk)

另设多项式方程 qka1qk1ak1qak=0(q>0,ak0) 的根是  q1,q2,,qk ,我们先讨论不存在重根的情况,也就是说k个根互不相等。

T(n) 的通解为:

T(n)=c1qn1+c2qn2++ckqnk

并且对于任意的初始情况

T(0)=b0T(1)=b1T(k1)=bk1

都存在一组解 c1,,ck 使得递推式成立

定理的证明

要证明以上定理,主要需要证明两部分。一是证明多项式根的线性组合可以满足递推式,二是证明任意初始条件下总有解。

可满足性证明

首先来证明 T(n)=c1qn1+c2qn2++ckqnk 可以满足递推式。

T(n)=a1T(n1)+a2T(n2)++ak1T(nk+1)+akT(nk) 经过变换可以改写为  T(n)a1T(n1)a2T(n2)ak1T(nk+1)akT(nk)=0

假设 T(n)=qn ,因为 q>0 ,所以两边除以 qnk ,得到 qka1qk1ak1qak=0

因此这个多项式和原递推式同解,因此多项式的每个根q的几何级数 qn 都是原递推式的一个解。同时,根的线性组合  c1qn1+c2qn2++ckqnk 均满足原递推式(可以带入验证)。

任意初始值有解证明

下面要证明对于任意初始条件,均存在适当的常数 c1,,ck

T(0)=b0T(1)=b1T(k1)=bk1

带入通解公式,得到一个线性方程组

c1+c2++ckc1q1+c2q2++ckqkc1q21+c2q22++ckq2kc1qk11+c2qk12++ckqk1k====b0b1b2bk1

此时问题转化为证明此方程组对于必然有解,下面就要用到线性代数的知识了。这个方程组的系数行列式为:

det(V)=1q1q21qk111q2q22qk121qkq2kqk1k

这个行列式就是非常著名Vandermonde行列式,所以

det(V)=1i<jk(qiqj)

上面我们假设了多项式各个根均互异,因此行列式的值不等于0,这意味着系数矩阵的秩为k。有线性代数知识可知,这表明 对于任意初始值 b0,,bk1 ,方程组均有唯一解。

证毕。

顺便说一下,上面的多项式叫特征多项式。其根叫特征根。

通用解法

通过上面的数学分析,我们得到了一个解线性递推式的通用方法。

齐次递推式

设有齐次递推式

T(n)=a1T(n1)+a2T(n2)++ak1T(nk+1)+akT(nk)

我们可以写出其特征多项式方程

qka1qk1ak1qak=0(q>0,ak0)

解出其k个根 q1,,qk 。如果k个根互异(但可以有复根),则原递推式的通解为

T(n)=c1qn1+c2qn2++ckqnk

然后将初始条件 b0,,bk1 带入组成线性方程组

x1+x2++xkx1q1+x2q2++xkqkx1q21+x2q22++xkq2kx1qk11+x2qk12++xkqk1k====b0b1b2bk1

解线性方程组得唯一解 x^1,,x^k 。带回通解公式则得到递推式的最终解

T(n)=x^1qn1+x^2qn2++x^kqnk

非齐次递推式

对于非齐次递推式

T(n)=a1T(n1)+a2T(n2)++ak1T(nk+1)+akT(nk)+C(n)(C(n)0)

可以首先按上面的方法求解其齐次部分的通解 Tn 。然后求得其一个特解 yn ,则非齐次递推式的通解为

Tn+yn

然后用同样的方法带入初始值,通过线性方程组求出个常量参数带回即可(具体可参见例2)。

有重根的情况

上面的解法只针对特征根互异,如果有重根的话,则上述方法会无效。不过只要经过一定处理也可以有通用方法求解, 因有点复杂,本文不在针对重根情况进行叙述。关于重根情况下的求解,有感兴趣的同学可以参考线性代数或微分方程相关文献。

相关阅读:

这篇关于算法分析中递推式的一般代数解法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/960312

相关文章

[职场] 公务员的利弊分析 #知识分享#经验分享#其他

公务员的利弊分析     公务员作为一种稳定的职业选择,一直备受人们的关注。然而,就像任何其他职业一样,公务员职位也有其利与弊。本文将对公务员的利弊进行分析,帮助读者更好地了解这一职业的特点。 利: 1. 稳定的职业:公务员职位通常具有较高的稳定性,一旦进入公务员队伍,往往可以享受到稳定的工作环境和薪资待遇。这对于那些追求稳定的人来说,是一个很大的优势。 2. 薪资福利优厚:公务员的薪资和

代码随想录算法训练营:12/60

非科班学习算法day12 | LeetCode150:逆波兰表达式 ,Leetcode239: 滑动窗口最大值  目录 介绍 一、基础概念补充: 1.c++字符串转为数字 1. std::stoi, std::stol, std::stoll, std::stoul, std::stoull(最常用) 2. std::stringstream 3. std::atoi, std

人工智能机器学习算法总结神经网络算法(前向及反向传播)

1.定义,意义和优缺点 定义: 神经网络算法是一种模仿人类大脑神经元之间连接方式的机器学习算法。通过多层神经元的组合和激活函数的非线性转换,神经网络能够学习数据的特征和模式,实现对复杂数据的建模和预测。(我们可以借助人类的神经元模型来更好的帮助我们理解该算法的本质,不过这里需要说明的是,虽然名字是神经网络,并且结构等等也是借鉴了神经网络,但其原型以及算法本质上还和生物层面的神经网络运行原理存在

高度内卷下,企业如何通过VOC(客户之声)做好竞争分析?

VOC,即客户之声,是一种通过收集和分析客户反馈、需求和期望,来洞察市场趋势和竞争对手动态的方法。在高度内卷的市场环境下,VOC不仅能够帮助企业了解客户的真实需求,还能为企业提供宝贵的竞争情报,助力企业在竞争中占据有利地位。 那么,企业该如何通过VOC(客户之声)做好竞争分析呢?深圳天行健企业管理咨询公司解析如下: 首先,要建立完善的VOC收集机制。这包括通过线上渠道(如社交媒体、官网留言

大林 PID 算法

Dahlin PID算法是一种用于控制和调节系统的比例积分延迟算法。以下是一个简单的C语言实现示例: #include <stdio.h>// DALIN PID 结构体定义typedef struct {float SetPoint; // 设定点float Proportion; // 比例float Integral; // 积分float Derivative; // 微分flo

LeetCode 算法:二叉树的中序遍历 c++

原题链接🔗:二叉树的中序遍历 难度:简单⭐️ 题目 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 示例 1: 输入:root = [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root = [] 输出:[] 示例 3: 输入:root = [1] 输出:[1] 提示: 树中节点数目在范围 [0, 100] 内 -100 <= Node.

【Java算法】滑动窗口 下

​ ​    🔥个人主页: 中草药 🔥专栏:【算法工作坊】算法实战揭秘 🦌一.水果成篮 题目链接:904.水果成篮 ​ 算法原理 算法原理是使用“滑动窗口”(Sliding Window)策略,结合哈希表(Map)来高效地统计窗口内不同水果的种类数量。以下是详细分析: 初始化:创建一个空的哈希表 map 用来存储每种水果的数量,初始化左右指针 left

打包体积分析和优化

webpack分析工具:webpack-bundle-analyzer 1. 通过<script src="./vue.js"></script>方式引入vue、vuex、vue-router等包(CDN) // webpack.config.jsif(process.env.NODE_ENV==='production') {module.exports = {devtool: 'none

ROS2从入门到精通4-4:局部控制插件开发案例(以PID算法为例)

目录 0 专栏介绍1 控制插件编写模板1.1 构造控制插件类1.2 注册并导出插件1.3 编译与使用插件 2 基于PID的路径跟踪原理3 控制插件开发案例(PID算法)常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 🚀详情:《ROS2从入门到精通》 1 控制插

算法与数据结构面试宝典——回溯算法详解(C#,C++)

文章目录 1. 回溯算法的定义及应用场景2. 回溯算法的基本思想3. 递推关系式与回溯算法的建立4. 状态转移方法5. 边界条件与结束条件6. 算法的具体实现过程7. 回溯算法在C#,C++中的实际应用案例C#示例C++示例 8. 总结回溯算法的主要特点与应用价值 回溯算法是一种通过尝试各种可能的组合来找到所有解的算法。这种算法通常用于解决组合问题,如排列、组合、棋盘游