- 个人主页:我的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
引言
本文通过两个案例来剖析递归与分治的求解思路。案例采用Go语言来实现,通过算法也能熟悉Go编程。
算法思想
分治的思想,也就是把规模较大的问题,拆分成规模较小,且解是相同的,以此不断拆分,直到容易得出解为止,再把各子问题合并成最终解。那这里的关键问题就是,如何拆成成同类型的子问题,很容易用到递归的方法。
递归方法能让这类问题处理起来简单,利用递归可以迭代的将问题不断拆分,每次只考虑子问题的处理;但同样带来计算机的性能问题,计算机通过递归栈来达到递进与回归,在递归深度不大时,与非递归差别较小,当递归深度大时,反而非递归算法性能更优。
阶乘
n的阶乘表示 n!=1*...(n-1)*n ; 其中 0!= 1 ,这是要特别注意的。
对于这个问题,我们可以进行拆分子问题,先求出(n-1)!,然后再与n相乘,就可以得到n!。
那么我们先用递归方来编码实现一下:
package main
import "fmt"
func factorial(n int)(res int) {
if n <= 1 {
return 1
}
return n*factorial(n-1)
}
func main() {
n := 5
f := factorial(n)
fmt.Println("n=",n,";factorial=",f)
}
用递归和分治思想的话,实现就相对简单,每次递归只关注当前子问题的解就可以。
能不能用非递归来实现呢?当然是可以的,下面用非递归方法编码:
func factorial(n int)(res int) {
if(n < 0) {
return 0
}
if(n == 0) {
return 1
}
f := 1
for i := 1; i <= n; i++ {
f *= i;
}
return f
}
非递归时,我们用了循环来累积,用上面的测试程序可以验证一下,结果是一样的。
斐波那契数
斐波那契数(Fibonacci sequence)也是一个经典的递归案例,又称黄金分割数列。
因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
其中 F(0) = 1, F(1) = 1,其后F(n) = F(n-1) + F(n-2)。
从公式表达来看,可以转换为先求子问题 n-1, n-2的解,再得出n的解,那我们用递归来实现一下。
package main
import (
"fmt"
)
func fabocci(n int) (res int) {
if n < 0 {
return 0
}else if n == 0 {
return 1
}else if n == 1 {
return 1
}
return fabocci(n-1)+fabocci(n-2)
}
func main() {
n := 9
f := fabocci(n)
fmt.Println("n=", n, "; fabocci=", f)
}
用递归和分治思想的话,实现就相对简单,每次递归只关注当前子问题的解就可以,这里是需要两个子问题的解才能得到更大子问题的解。
能不能用非递归来实现呢?当然是可以的,下面用非递归方法编码:
func fabocci(n int) (res int) {
if n < 0 {
return 0
}else if n == 0 {
return 1
}else if n == 1 {
return 1
}
f, fn1, fn2 := 0, 1, 1 // fn1-> f(n-1),fn2 -> f(n-2)
for i := 2; i <= n; i++ {
f = fn1 + fn2
fn1 = fn2
fn2 = f
}
return f
}
非递归时,我们依然使用了循环,但是这里用了两个fn1,fn2来记录上两次的计算结果,不断交替赋值,达到更新上两次的最新结果。
用上面的测试程序可以验证一下,结果是一样的。
结尾
作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!