递推方程的求解

递推方程的求解

其实这是本人《算法设计与分析》课程回顾的内容整理,用来测试一下cnblogs上的markdown和数学公式支持......

什么是递推方程?

对于序列\(a_0,a_1,a_2, …,a_n\),简记为\({a_n }\),一个把\(a_n\)与若干个\(a_i (i<n)\)联系在一起的等式叫做关于序列\({a_n}\)的递推方程。

为什么要学习求解递推方程?

因为对递归算法的分析离不开递推方程的求解

例如,Hanoi塔问题递归算法为:

Hanoi(A, C, n):                # 将A柱上的n个盘子按照要求移到C柱上
    if n == 1:                 # 只有一个盘子就直接移到C柱s上
        Move(A, C)
    else:
        Hanoi(A, B, n – 1)     # 否则先把上面n-1个盘子移到B柱上
        Move(A, C)             # 再把最下面的盘子移到C柱上
        Hanoi(B, C, n – 1)     # 再把那n-1个盘子移到C柱上

分析可得递推方程为

\[
\begin{cases}
T(n) = 2T(n-1) + 1\\
T(1) = 1
\end{cases}
\Rightarrow T(n) = 2^n - 1
\]

有哪些求解方法?

  1. 迭代法

    直接迭代:直接将递推式逐层迭代展开,找到求和式规律求解。

    换元迭代:直接迭代展开不方便时,换元成便于直接迭代展开的形式再求解。但这样往往需要对结果进行验证。

    差消迭代:当递推式较为复杂,关联的前项较多时,可以通过将临近递推式适当变形并相减来化成可以迭代求解的形式。

    迭代模型:递归树是对迭代法的直观描述

  2. 尝试法: 没有办法的办法
  3. 主定理: 是对一类可以迭代求解的递推方程的总结

直接迭代法举例:插入排序最坏情况的复杂度?

插入排序伪代码描述:

# 给定n个数的数组A,输出排序好的数组A
InsertionSort(A, n):
    for j = 2 to n:
        x = A[j]
        i = j - 1
        while i > 0 and x < A[j]:
            A[i + 1] = A[i]
            i = i – 1
        A[i + 1] = x

最坏情况下,在总共的\(n\)次迭代中,每次都需要和前面的\(n-1\)个元素进行比较才能确定插入的位置,那么对于\(n\)个元素,总的比较次数为前面\(n-1\)个元素的比较次数再加上自己的\(n-1\)次比较次数,即得递推方程

\[
\begin{cases}
T(n) = T(n-1) + n - 1 \T(1) = 0
\end{cases}
\]

这样的递推方程解法非常直观,直接逐次迭代,找到和式规律来求解即可。

\[
\begin{align}
T(n) &= T(n - 1) + n - 1 \&= T(n-2)+n-2+n-1 \&=... \&= T(1) + \sum_{i=1}^{n-1}i \&= n(n-1)/2
\end{align}
\]

这样我们就得到了插入排序最坏情况下的复杂度为\(O(n^2)\)。

换元迭代法举例:二分归并排序最坏情况的复杂度?

二分归并排序伪代码描述:

# 对于给定的数组A,将A[p]-A[r]范围内的数排好序
MergeSort(A, p, r):
    if p < r:
        q = (p + r) / 2
        Mergesort(A, p, q);
        MergeSort(A, q + 1, r)
        Merge(A, p, q, r)

# 将按照递增顺序排列好的A[p...q]和A[q+1...r],输出排序好的数组A[p...r]
Merge(A, p, q, r):
    x = q - p + 1, y = r - q
    将A[p...q]复制到B[1...x], 将A[q+1...r]复制到C[1...y]
    i = 1, j = 1, k = p
    while i <= x and j <= y:
        if B[i] <= C[j]:
            A[k] = B[i]
            i = i + 1
        else:
            A[k] = C[j]
            j = j + 1
        k = k + 1
    if i > x:
        将C[j...y]复制到A[k...r]
    else:
        将B[i...y]复制到A[k...r]

对于归并排序的算法分析中,如果假设输入规模\(n=2^k\),就可以比较容易地进行分析。

上述算法对于\(n=2^k\)的数组,两次递归调用分别对\(n=2^{k-1}\)规模的子问题求解,最后的归并操作,在最坏情况下需要\(n-1\)次比较运算,则可得递推方程为

\[
\begin{cases}
T(n) = 2T(n/2) + n - 1 \T(1) = 0
\end{cases}
\]

对于这样的递推方程,为了便于使用迭代法求解,不妨先设\(n=2^k\),当然,这样设相当于我们求的只是递推方程在一部分\(n\)值下的解,因此在求出结果后应当带入原递推方程验证结果。这种先换元成一种容易求解的形式再迭代求解的方法称为换元迭代法。

\[
\begin{align}
T(2^k) &= 2T(2^{k-1}) + 2^k - 1 \&= 2(T(2^{k-2}) +2^{k-1} - 1) + 2^k - 1 \&= 4T(2^{k-2})+ 2*2^k - 2 - 1 \&= ... \&= 2^kT(1) + k2^k - \sum_{i=0}^{k-1}2^i \&= k2^k - 2^k + 1 \&= n\log n - n + 1
\end{align}
\]

将其带入原来的递推方程可得

\[
\begin{cases}
T(1) = 1log1 - 1 + 1 = 0 \2T(n/2) + n - 1 = nlog(n/2) - n + 2 + n - 1 = nlogn - n + 1 = T(n)
\end{cases}
\]

从而验证了我们的解是满足原来的递推方程的。

这样通过以上的分析就证明了二分归并排序在最坏情况下的复杂度为\(O(nlogn)\)。

差消迭代法举例:对快速排序平均情况复杂度的分析

# 将A[p...r]排序
QuickSort(A, p, r):
    if p < r:
        q = Partition(A, p, r)    # 划分数组找到首元素A[p]在排好序后的位置q
        swap(A[p], A[q])
        QuickSort(A, p, q - 1)
        QuickSort(A, q + 1, r)

# 对于数组A[p...r],将A[p]放到适当的位置i使得A[p...i-1]都小于A[i], A[i+1...r]都大于A[i]
Partition(A, p, r):
    x = A[p]
    i = p
    j = r
    while i < j:
        while A[j] >= x:
            j = j - 1
        if i >= j:
            break
        else:
            A[i] = A[j]
            i = i + 1
        while A[i] <= x:
            i = i + 1
        if i >= j:
            break
        else:
            A[j] = A[i]
            j = j - 1
    A[i] = x
    return i
           

我们目前只考虑平均情况的分析,假设数组\(A\)的首元素在排好序后处在\(n\)个位置中的任何位置都是可能的,即它处在任何位置的概率都是\(1/n\),如果它处在位置\(i(i=1,2,..,n)\)那么两个子问题的规模分别为\(i-1\)和\(n-i\). 考虑到\(T(0)=0\),\(T(1) = 0\),因此可以得到

\[
\begin{align}
T(n) & = \frac{1}{n}\{[T(0) + T(n-1) ] + [T(1) + T(n-2)] + ... + [T(n-1) + T(0)]\} + O(n) \&= \frac{2}{n}\sum_{i=1}^{n-1}T(i) + O(n)
\end{align}
\]

此外,由Partition()算法,可以看到比较次数最坏情况下为\(n-1\),因此,不妨设最后一项为\(n-1\),从而得到如下递推公式

\[
\begin{cases}
T(n) = \frac{2}{n}\sum_{i=1}^{n-1}T(i) + n-1 \T(1) = 0
\end{cases}
\]

当递推式较为复杂,关联的前项较多时,可以通过将临近递推式适当变形并相减来化成可以迭代求解的形式。因此发现可以稍作变形得到

\[
nT(n) = 2\sum_{i-1}^{n-1}T(i) + n^2 - n \(n-1)T(n-1) = 2\sum_{i =1}^{n-2}T(i) + (n-1)^2 - (n-1)
\]

将两个方程相减得到(这样求和符号就被消掉了)

\[
nT(n) - (n-1)T(n-1) = 2T(n-1)+2n-2
\]

化简得到

\[
nT(n)=(n+1)T(n-1) + 2n-2
\]

再次变形

\[
\begin{align}
\frac{T(n)}{n+1} &= \frac{T(n-1)}{n} + \frac{2n-2}{n(n+1)} \&= \frac{T(n-1)}{n} + \frac{2}{n+1} - \frac{2}{n(n+1)} \\
&= ... \&=2\sum_{i=3}^{n+1}\frac{1}{i} + \frac{T(1)}{2} + O(\frac{1}{n}) \\end{align} \\because \sum_{i=3}^{n+1}\frac{1}{i} = \Theta(\log n)(可用积分证明) \therefore T(n) = O(n\log n)
\]

迭代模型:递归树

递归树是一种对上述迭代求解的思想的直观描述。

比如对于二分归并排序的方程求解过程就可以画成递归树

又例如:对于以下递推方程

\[
T(n) = T(n/3) + T(2n/3) + n
\]

? n

? n/3 2n/3 n

n/9 2n/9 2n/9 4n/9 n

可得这样的递归树,最右边的路径就是最长的路径,这路径共有\(\log_{3/2}n\)层,每层复杂度都为\(O(n)\),易得整个递推方程的复杂度为\(O(nlogn)\)。

尝试法:对快速排序递推方程的求解

之前我们得到过形式为这样的快速排序递推方程,将\(O(n)\)设为\(n-1\)以便用迭代法求解

\[
T(n)= \frac{2}{n}\sum_{i=1}^{n-1}T(i) + O(n)
\]

那如果直接对这种形式求解呢?一种没有办法的办法就是估计复杂度带入两边尝试。

  1. 如果\(T(n)=cn\),右边\(=\frac{2}{n}\sum_{i=1}^{n-1}ci + O(n) = cn - c + O(n)\)

两边虽然最高项都是一次项,但是右边一次项的系数更大(因为\(O(n)\)中还有一次项系数),不成立。

  1. 如果$T(n) = cn^2 $,

\[
\begin{align}
{右边}&=\frac{2}{n}\sum_{i=1}^{n-1}ci^2 + O(n) \&= \frac{2c}{n}(\frac{n^3}{3} + O(n^2)) + O(n) \ \ \ \
\because \sum_{i=1}^{n-1}((i+1)^3-i^3))=n^3-1=3\sum_{i=1}^{n-1}i^2+3\sum_{i=1}^{n-1}i+n-1) \&= \frac{2c}{3}n^2 + O(n) \ne {左边}
\end{align}
\]

  1. 如果\(T(n) = cn\log n\),

    \[
    \begin{align}
    {右边} &= \frac{2c}{n}\sum_{i=1}^{n-1}i\log i + O(n) \ & =\frac{2c}{n}[\frac{n^2}{2}\log n - \frac{n^2}{4\ln 2} + O(1)] + O(n) \ \ \ \ ({积分法可证}) \ & =cn\log n + O(n)
    \end{align}
    \]

    这时左边和右边的最高次项系数相同,因此\(T(n)=O(n\log n)\)。

主定理:总结了一类可以迭代求解的递推方程

设\(a\ge 1,b>1\)为常数,\(f(n)\)为函数,\(T(n)\)为非负整数,且

\[
T(n) = aT(n/b) + f(n)
\]

则有以下结果:

(1) 若\(f(n) = O(n^{\log_ba-\varepsilon}),\varepsilon>0\),那么\(T(n) = \Theta(n^{\log_ba})\)

(2) 若\(f(n)=\Theta(n^{\log_ba})\),那么\(T(n) = \Theta(n^{\log_ba}\log n)\)

(3) 若\(f(n) = \Omega(n^{log_ba+\varepsilon}),\varepsilon>0\),且对于某个常数\(c<1\)和所有充分大的\(n\)有\(af(n/b)\le cf(n)\),那么\(T(n)=\Theta(f(n))\).

证明的要点:设\(n=b^k\)利用迭代法推导即可。情况(3)必须使用附加条件才能推出结果。

需要注意的要点:

(1)(3)中的\(\varepsilon\),事实上是为了使得\(f(n)\)的阶严格大或者严格小。

情况(3)中的附加条件需要额外注意。

使用主定理的例子

归并排序中的递推式:

\[
\begin{cases}
T(n) = 2T(n/2) + n - 1 \T(1) = 0
\end{cases}
\]

属于主定理情况(2),使用主定理直接得$ T(n)=O(n^{\log_22}\log n)=O(n\log n)$.

不能使用主定理的例子

\[
\begin{cases}
T(n) = 2T(n/2) + n\log n \T(1) = 1
\end{cases}
\]

根据主定理,\(a=b=2\),\(f(n)=n\log n\),但是找不到\(\varepsilon\)使得\(f(n)=\Omega(n^{1+\varepsilon})\)成立。

使用递归树的方法可以求解得结果等于\(T(n) = \Omega(n\log^2 n)\)

递推方程中\(\lfloor x \rfloor\)和\(\lceil x \rceil\)的处理

现猜想解,然后用数学归纳法证明

例如对于以下递推关系:

\[
\begin{cases}
T(n)= 2T(\lfloor \frac{n}{2} \rfloor) + n \T(1) = 1
\end{cases}
\]

若没有取整符号,根据主定理即得其阶为\(O(n\log n)\)

因此猜想\(T(n)=O(n\log n)\),下面证明\(T(n)\le cn\log n\):

这里使用数学归纳法。

对于\(T(1)=1, T(1) <c\ 1 \log 1\)不成立

对于\(T(2)=2T(\lfloor 1 \rfloor) + 2 = 4 \le 2* 2\log 2\)

假设对于小于\(n\)的正整数命题为真,那么

\[
\begin{align}
T(n)&=2T(\lfloor \frac{n}{2} \rfloor) + n \&\le 2c\lfloor \frac{n}{2} \rfloor log(\lfloor \frac{n}{2} \rfloor) + n \&\le 2c\frac{n}{2}(\log n - \log 2) + n \&= cn\log n - cn + n \&\le cn\log n, \ c = 2
\end{align}
\]

证毕。

参考文献 《算法设计与分析》 北京大学出版社

原文地址:https://www.cnblogs.com/hehao98/p/8516050.html

时间: 2024-10-13 16:00:24

递推方程的求解的相关文章

算法分析基础——差消法求解高阶递推方程

差消法,简单来讲,就是对高阶的递推方程作差,转化为一阶方程后再运用迭代法.有了迭代法的基础后,差消法理解起来就很容易了.这里举出对快速排序的分析加以说明. 对于快排,我们知道选择不同的轴值,会导致不同的算法效率.最坏的情况下,选取的轴值恰好是待排序数组的最值,那么排序的效率就会退化为线性时间.现在我们来估算平均情况下快速排序的时间复杂度. 设T(n)为待排序数组长度为n时,快速排序算法需要的比较次数.那么T(1) = 0,而T(n)的递推方程相对于轴值的选取有如下n种情况: T(n) = T(0

hdu2045 不容易系列三——LELE的RPG难题 (递推方程)

本文出自:blog.csdn.net/svitter 原题:http://acm.hdu.edu.cn/showproblem.php?pid=2045 题意:中文不用我说了吧. 这个题目的关键就在于递推方程--以及错误的测试数据 首先这个题目就是简单的置换群着色问题-- 去除了反转的问题,难一点的大家可以看P197(离散数学,高等教育出版社) 我在做这个题目的时候首先被f [ 1 ]  = 3 困扰了..拜托,根本不符合题意好吗- =一个格子能说是首尾颜色不同吗? 后来写错了递推方程--f [

hdu1465 不容易系列之一 (递推方程)

本文出自:http://blog.csdn.net/svitter 题意:错排情况的数量,打表求错排数即可. 错排数公式:f [ n ] = ( n - 1 ) * ( f [ n - 1 ] + f [ n - 2] ); 也可以这么想; (1).f [ 1 ] = 0 ; f [ 2 ] = 1; (2).如果确定f [ n - 1 ] 和 f [ n - 2 ] 的话. .f [ n ] 中必然包含 f [ n - 1] * ( n - 1 )种情况. 即把新加入的一封和之前的任一一封交换

递推(二):递推法的应用

下面通过一些典型实例及其扩展来讨论递推法的应用. [例2]骨牌铺方格 在2×n的一个长方形方格中,用一种2×1的骨牌铺满方格.输入n(n<=40),输出铺放方案的总数. 例如n=3时,为2×3方格,骨牌的铺放方案有三种,如下图1所示. 图1  2×3方格的骨牌铺放方案 (1)编程思路. 设f[i]为铺满2*n方格的方案数,则有    f[i]=f[i-1]+f[i-2]. 其中,f[i-1]为铺满2*(n-1)方格的方案数(既然前面的2*(n-1)的方格已经铺满,那么最后一个只能是竖着放).f[

uva live 4123 Glenbow Museum 数学递推

// uva live 4123 Glenbow Museum 数学递推 // // 题目大意: // // 对于一个边平行于坐标轴的多边形,我们可以用一个序列来描述,R和O,R表示 // 该顶点的角度为90度,O表示该定点的角度为270.给定序列的长度.问能由这些RO序 // 列组成的星型多边形(内部存在一个点可以看到所有的节点的多边形)的方法数有多少. // // 解题思路: // // 顶点数一定是序列的长度.n小于4无法组成多边形.n为奇数的时候也不行.设R的个数有 // x个,O的个数

1025 数的划分(搜索和递推方法)

难度:普及/提高- 题目类型:递推 提交次数:3 涉及知识:动规 题目描述 将整数n分成k份,且每份不能为空,任意两个方案不相同(不考虑顺序). 例如:n=7,k=3,下面三种分法被认为是相同的. 1,1,5; 1,5,1; 5,1,1; 问有多少种不同的分法. 输入输出格式 输入格式: n,k (6<n<=200,2<=k<=6) 输出格式: 一个整数,即不同的分法. 搜索: 代码: 1 #include<iostream> 2 using namespace std

递推专题笔记

递推说白了就是找规律,然后写出他的递推方程,有的还可以写出通项公式,然后准确预测出第n项的值.因为这种规律存在着前因后果的关系,即是说,后一项的结果往往和前一项或前几项有着某种联系.这种联系不仅仅存在于数字之中,世间万物亦是如此. 由于,递推是深入理解动态规划的基础,就我目前的水平,看到动态规划就如看到tiger一般,完全不知所以,所以为了找回在动态规划前的自信,我打算在回家之前,找一个递推专题练练手.现记录如下: 心得: 1. 既然是递推题,那么肯定有递推方程,那么同样有规律可循,既然有规律.

【基础练习】【贪心】【递推】NOIP2013提高组第五题 积木大赛题解

还是先把题目放上吧: 春春幼儿园举办了一年一度的"积木大赛".今年比赛的内容是搭建一座宽度为n的大厦,大厦可以看成由n块宽度为1的积木组成,第n块积木的最终高度需要是hi. 在搭建开始之前,没有任何积木(可以看成n块高度为 0 的积木).接下来每次操作,小朋友们可以选择一段连续区间[L,R],然后将第L块到第R块之间(含第 L 块和第 R 块)所有积木的高度分别增加1. 小M是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少.但她不是一个勤于动手的孩子,所以想

百度之星-大搬家-递推

问题描述: 近期B厂组织了一次大搬家,所有人都要按照指示换到指定的座位上.指示的内容是坐在位置ii上的人要搬到位置jj上.现在B厂有NN个人,一对一到NN个位置上.搬家之后也是一一对应的,改变的只有位次. 在第一次搬家后,度度熊由于疏忽,又要求大家按照原指示进行了一次搬家.于是,机智的它想到:再按这个指示搬一次家不就可以恢复第一次搬家的样子了.于是,B厂史无前例的进行了连续三次搬家. 虽然我们都知道度度熊的“机智”常常令人堪忧,但是不可思议的是,这回真的应验了.第三次搬家后的结果和第一次的结果完