解递归式的方法总结

转载自:http://blog.youkuaiyun.com/a130098300/article/details/7632227

算法设计中经常会用到递归,利用递归式的方法可以清晰地显示算法的整个过程,而对于分析算法的复杂度,解递归式就有了用处,这里的方法来自于《算法导论》。

(一)代换法:

实质上就是数学归纳法,先对一个小的值做假设,然后推测更大的值得正确性。由于是数学归纳法,那么我们就需要对值进行猜测。现在,我们看下面这个例子:


我们先假设一个结论T(n) = O(lg(n - b)),并且假设对T(n / 2上取整)成立(这就是数学归纳法了),那么把T(n / 2上取整)用假设的结论进行代换,我们有T(n) <= lg((n - b)) / 2上取整)

<= lg((n - b) / 2 + 1) + 1 = lg(n - b + 2),对于任意的b >= 2,即满足要求T(n) <= lg(n) = O(lgn)。证毕。

这是一个很简单的例子,但是其中有些事情还是要交代的。

一个是结论的猜测不是一个容易的事情。另一个是在上面的例子中我没有直接下结论说T(n) = O(lg(n)),而是减去了一个常数b,这是为什么呢?答案是:we can prove something stronger for a given value by assuming something stronger for smaller values.还有一点值得说明,请看下面这个例子:


这里出现了sqrt(n),我们采用变量代换的方法:令n = 2 ^ m,则上式变为T(2 ^ m) = 2T(2 ^ (m / 2) ),再设S(m) = T(2 ^ m),则得到S(m) = 2S(m / 2) + 1。我们先假设S(m) = m - b <= O(m),且对S(m / 2)成立,那么S(m) = 2 * (m - b) / 2 + 1 = m - b + 1,对于任意的b >= 1都有S(m) = O(m),然后回代有T(n) = T( 2 ^ m) = S(m) = O(m) = O(lgn)。

这里我们采用了变量代换的方法。如果假设时,感觉变量不明朗,那么这是一种很有效的方法。

(二)递归树方法:

利用递归树方法求算法复杂度我想最好的例子就是归并排序了,这里我不想拿归并排序做例子,而只是用书中一些更简单形象的例子来说明:



根据上式我们建立递归式T(n) = 3T(n / 4) + cn^2,这里我们抛去上下界函数的影响(sloppiness that we can tolerate),并且把Theta(n ^ 2)用cn^2代替。下面建立递归树模型:




在递归树中,每一个结点都代表一个子代价,每层的代价是该层所有子代价的总和,总问题的代价就是所有层的代价总和。

所以,我们利用递归树求解代价,需要知道什么呢,一个是每一层的代价,一个是层数,就是这两个。

这些,都需要我们用觉察的态度来发现,而事实证明,这不是一件难事,很多情况下是有规律可循的。

我们且看上面这个例子,递归树的构造很简单,当递归调用到边界是,就达到了常量T(1),达到常量T(1)所用到的递归次数就是整个递归树的深度,我们从图中可以得到第i层的结点的代价为n / ( 4 ^ i ),当n / (4 ^ i) = 1即i = log4(n)时,递归到达了边界,所以,整个递归树的深度就是i = log4(n)。我们要求的总的代价是所有的总和,结果为O(n ^ 2)。计算过程我就不再累述了。但是,递归树并不是都是这样的满的树,也就是不是每一层的结点都是相同的结构,所以我们在构造递归树的时候要仔细看好这一点,才能保证在计算时不会出错。

(三)主方法:

其实应该叫做主定理方法,利用这个方法,我们只需要记住主定理的三种情况,并且在满足一定的条件下,就可以速度解出递归式。主定理的三种情况不在这里给出,一定条件我只说一下我对于多项式大于(或小于)的理解,比如x和1.1x,那么x就是多项式小于1.1x,二者差了一个多项式(0.1x),而至于x与xlgx就不存在多项式大于(或小于)的关系。

这个方法很直截了当,我在学习这个方法的时候其实就是从课后练习入主。但是三种情况,我却无法牢记。


<think>嗯,用户问的是循环赛日程表如何列出递归式。首先,我得回忆一下循环赛的基本概念。循环赛是指每个参赛队伍都要和其他所有队伍比赛一次,所以总共有n-1轮比赛,每轮每队比赛一次。日程表需要安排这些比赛,确保每轮的对阵都没有重复。 那递归式的话,应该和分治法有关吧。比如,当队伍数是2的幂次时,可以用分治策略,把问题分成更小的子问题。比如,将2^k个队伍分成两组,每组2^(k-1)个,先安排组内的比赛,再安排组间的。这时候递归式可能涉及到子问题的大小和合并的步骤。 比如,假设T(n)是安排n个队伍的时间复杂度,那么当n是2的幂次时,分成两个n/2的子问题,每个子问题需要T(n/2),然后合并需要O(n)的时间。这样递归式应该是T(n) = 2T(n/2) + O(n)。不过这里可能需要具体考虑合并的过程,比如每个队伍在合并阶段需要与另一组的每个队伍比赛,所以合并的时间复杂度可能更高? 不过用户可能更关心的是如何构造递归的步骤,而不仅仅是时间复杂度。比如,当n=2时,直接安排两队比赛。当n=4时,分成两个两组,先安排每组内部,然后交叉安排两组之间的比赛。这时候可能需要复制或镜像已有的安排,来生成更大的表。 所以递归式的构造可能基于将问题分为更小的子问题,然后合并结果。比如,对于n=2^k,每次递归地将问题分成两半,直到基线条件n=2,然后合并的时候填充交叉对战的安排。 可能需要举例子说明,比如n=4的情况,如何通过n=2的情况扩展得到。然后推广到一般情况,得出递归的表达式。比如,每次将日程表分成四个子块,左上和右上是子问题的,左下是右上的复制,右下是左上的复制,或者其他方式。 不过需要注意,当n不是2的幂次时,可能需要调整,但用户的问题可能假设n是2的幂次,因为递归分治的方法通常适用于这种情况。所以递归式的推导可能基于分治策略,每次将规模减半,合并时需要线性时间或者平方时间,具体取决于合并的步骤。 总结一下,可能的步骤是:分问题为两个子问题,决子问题,合并结果。而递归式可能表示为T(n) = 2T(n/2) + O(n^2)或者类似的,但需要具体分析合并阶段的复杂度。不过用户可能需要的是如何结构性地写出递归关系,而不仅仅是复杂度分析。因此,应该详细描述递归分的过程,比如如何将2k个队伍分成两组,安排内部比赛后,再安排组间比赛,从而形成递归结构。</think>循环赛日程表的递归式推导可以通过分治法逐步分问题。以下是详细步骤: --- ### **1. 问题理** 循环赛要求每个队伍与其他所有队伍比赛一次。设队伍数为$n=2^k$(保证递归分),需生成$n-1$轮比赛的日程表。 --- ### **2. 基线条件(Base Case)** 当$n=2$时,直接安排: ``` 队伍1 vs 队伍2(第1轮) ``` 递归终止。 --- ### **3. 递归分(Divide)** 假设已决$n/2$个队伍的日程表,将$n$个队伍分为两组: - **组A**:队伍$1,2,\dots,n/2$ - **组B**:队伍$n/2+1,\dots,n$ 递归生成组A和组B的内部日程表。 --- ### **4. 合并策略(Conquer)** 合并两组时,需安排组A与组B之间的比赛: 1. **复制子问题**:将组A和组B的日程表分别填入大表的左上和右上象限。 2. **填充下半部分**: - **左下象限**:复制右上象限的对阵,但交换主客场。 - **右下象限**:复制左上象限的对阵,同样交换主客场。 --- ### **5. 递归式形式化** 设$T(n)$为生成$n$队日程表的时间复杂度: $$ T(n) = 2T\left(\frac{n}{2}\right) + O(n^2) $$ - **$2T(n/2)$**:递归处理两个子问题。 - **$O(n^2)$**:合并时填充下半部分需遍历$n/2 \times n/2$的矩阵。 --- ### **6. 实例演示(n=4)** 1. 分为两组:{1,2}和{3,4}。 2. 递归生成子表: ``` // 组A日程表 1 vs 2 // 组B日程表 3 vs 4 ``` 3. 合并后的大表: ``` 1-2 | 3-4 2-1 | 4-3 ------------- 3-4 | 1-2 4-3 | 2-1 ``` --- ### **7. 关键点总结** - **分治结构**:将问题拆为两个子问题,合并时填充交叉比赛。 - **递归式推导**:合并阶段的$O(n^2)$复杂度主导递归式。 - **适用条件**:仅当$n=2^k$时直接适用,否则需调整(如补足虚拟队伍)。 通过这种递归分治策略,可高效生成循环赛日程表。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值