14天阅读挑战赛
第1篇:读《趣学算法》:重开算法之门,时间复杂度与空间复杂度
继续读《趣学算法》,这一节,读到了神奇的兔子数列,让我们将算法的改进,进行到底~。
描述:假设有一对出生的兔子,第二个月进入成熟期,第三个月开始生育兔子,而每对兔子每月能生一对兔子,兔子永不会死去,……,那么由第一对兔子开始,12个月后会有多少对兔子呢?
第1个月:1对A新生兔子 (总计1对)
第2个月:1对A成熟的兔子 (总计1对)
第3个月:1对A成熟的兔子+1对AB新生兔子 (总计2对)
第4个月:1对A成熟的兔子+1对AC新生兔子+1对AB成熟兔子 (总计3对)
第5个月: 1对A成熟的兔子+1对AD新生兔子+1对AC成熟兔子+1对AB成熟兔子+ABA新生兔子(总计5对)
第6个月:1对A成熟+1对AE新生+1对AD成熟+1对AC成熟+1对ACA新生+1对AB成熟+1对ABB新生+1对ABA成熟 (总计8对)
第7个月:1对A成熟+1对AF新生+1对AE成熟+1对AD成熟+1对ADA新生+1对AC成熟+1对ACB新生+1对ACA成熟+1对AB成熟+1对ABC新生+1对ABB成熟+1对ABA成熟+1对ABAA新生 (总计13对)
……
从1月开始……第7月依次为:1对、1对、2对、3对、5对、8对、13对
所以可知:从第3个月开始(包含第3个月),之后第n个月兔子的数量是是(n-1)+(n-2)对,列出函数为:
f ( n ) = { 1 n < 2 f ( n − 1 ) + f ( n − 2 ) n ≥ 3 f(n)=\begin{cases} 1 & n<2 \\ f(n-1)+f(n-2) & n\geq3 \\ \end{cases} f(n)={1f(n−1)+f(n−2)n<2n≥3
由此可知,最直接的算法,可以通过递归函数来实现
提示:简单描述算法知识点相关题目题意
#include
#include
int fib(int n)
{
if(n <= 2)
{
printf("n=%d, 有(%d)对兔子, \n", n, 1);
return 1;
}
else
{
int sum = fib(n-1) + fib(n-2);
printf("n=%d, 有(%d)对兔子, \n", n, sum);
return sum;
}
}
int main(int argc, char **argv)
{
int n = atoi(argv[1]);
printf("计算斐波那契数列,第%d个月有多少个兔子? \n", n);
int sum = fib(n);
printf("总计:%d对\n", sum);
return 0;
}
szhou@bc04:~/Test$ gcc -o fib Fibonacci.c
szhou@bc04:~/Test$ ./fib 7
计算斐波那契数列,第7个月有多少个兔子?
n=2, 有(1)对兔子,
n=1, 有(1)对兔子,
n=3, 有(2)对兔子,
n=2, 有(1)对兔子,
n=4, 有(3)对兔子,
n=2, 有(1)对兔子,
n=1, 有(1)对兔子,
n=3, 有(2)对兔子,
n=5, 有(5)对兔子,
n=2, 有(1)对兔子,
n=1, 有(1)对兔子,
n=3, 有(2)对兔子,
n=2, 有(1)对兔子,
n=4, 有(3)对兔子,
n=6, 有(8)对兔子,
n=2, 有(1)对兔子,
n=1, 有(1)对兔子,
n=3, 有(2)对兔子,
n=2, 有(1)对兔子,
n=4, 有(3)对兔子,
n=2, 有(1)对兔子,
n=1, 有(1)对兔子,
n=3, 有(2)对兔子,
n=5, 有(5)对兔子,
n=7, 有(13)对兔子,
总计:13对
szhou@bc04:~/Test$
szhou@bc04:~/Test$ ./fib 12
计算斐波那契数列,第12个月有多少个兔子?
……省略……
n=12, 有(144)对兔子,
总计:144对
szhou@bc04:~/Test$
int fib(int n)
{
if(n <= 2)
{
printf("n=%d, 有(%d)对兔子, \n", n, 1);
return 1;
}
else
{
int sum = fib(n-1) + fib(n-2);
printf("n=%d, 有(%d)对兔子, \n", n, sum);
return sum;
}
}
分析int fib(int n)这个递归函数的方法,其一是按照高等数学的方法,求出斐波那契数列的通项公式,这个对于我来说,忘得太干净。还是走第二条路,画出此函数的计算过程,可以发现它是一个树,可以称之为递归树,下面的每一个圈圈,都是一次计算!为了推导方便,假设n=5。
分析结果:时间复杂度可表示为 O( 2 n / 2 2^{n/2} 2n/2)
改进思路:从斐波那契数列的特点,从第3项开始,后面每一项都是前两项的和,我们可以做一个数组,将每次的计算结果都存储起来,这样只需要执行大约n次,即可达到结果。
#include
#include
int fib_pro(int n)
{
int *p = malloc(sizeof(int) * (n+1)); //计算1次
p[1] = 1; //计算1次
p[2] = 1; //计算1次
for(int i=3; i<=n; i++)
{
p[i]=p[i-1]+p[i-2]; //计算n-2次
}
return p[n];
}
int main(int argc, char **argv)
{
int n = atoi(argv[1]);
printf("计算斐波那契数列,改进型算法,第%d个月有多少个兔子? \n", n);
int sum = fib_pro(n);
printf("总计:%d对\n", sum);
return 0;
}
szhou@bc04:~/Test$ gcc -o fib_pro Fibonacci.c
szhou@bc04:~/Test$ ./fib_pro 7
计算斐波那契数列,改进型算法,第7个月有多少个兔子?
总计:13对
szhou@bc04:~/Test$ ./fib_pro 12
计算斐波那契数列,改进型算法,第12个月有多少个兔子?
总计:144对
szhou@bc04:~/Test$
从楼上改进算法,我们创建了一个n+1的数组空间,但其实我们只需要最后一个值,所以我们还可以对存储空间,即算法的空间复杂度做一下改进,将空间复杂度从 O ( n ) O(n) O(n)降低为 O ( 1 ) O(1) O(1)
#include
#include
int fib_xPro(int n)
{
int temp_1 = 1;
int temp_2 = 1;
int temp_3 = 1;
for(int i=3; i<=n; i++)
{
temp_3 = temp_1 + temp_2;
temp_1 = temp_2;
temp_2 = temp_3;
}
return temp_3;
}
int main(int argc, char **argv)
{
int n = atoi(argv[1]);
printf("计算斐波那契数列,改进型算法,第%d个月有多少个兔子? \n", n);
int sum = fib_xPro(n);
printf("总计:%d对\n", sum);
return 0;
}
szhou@bc04:~/Test$ gcc -o fib_xPro Fibonacci.c
szhou@bc04:~/Test$ ./fib_xPro 7
计算斐波那契数列,改进型算法,第7个月有多少个兔子?
总计:13对
szhou@bc04:~/Test$ ./fib_xPro 12
计算斐波那契数列,改进型算法,第12个月有多少个兔子?
总计:144对
szhou@bc04:~/Test$
如有错误,帮忙指正一下哈~