1.斐波那契
關於斐波那契數列的定義和應用網上已經有一大堆介紹了,可以去查一查。我以前只知道用循環和遞歸這兩種方式求,但是最近發現一種矩陣乘方的方式來求解。因此就用C語言實現了這3中方式。
- 求斐波那契第n個數,循環
- 求斐波那契第n個數,遞歸(n大了后會有很多重復運算,不推薦)
- 矩陣法計算
#include <stdio.h>
#include <stdlib.h>
// 求斐波那契第n個數,循環
long long offer74_fibonacci0(unsigned n)
{
if (n <= 1)
return n;
long long f0 = 0,f1 = 1, fn;
while (--n)
{
fn = f0 + f1;
f0 = f1;
f1 = fn;
}
return fn;
}
// 求斐波那契第n個數,遞歸(n大了后會有很多重復運算,不推薦)
long long offer74_fibonacci1(unsigned n)
{
return n <= 1 ? n : offer74_fibonacci1(n-1)+offer74_fibonacci1(n-2);
}
//-----------------------------------------------
// 矩陣乘方的計算方式
typedef struct Matrix2By2_t
{
long long m00,m01,m10,m11;
} Matrix2By2;
// 初始化一個矩陣
Matrix2By2 *InitMatrix(long long m00,long long m01,long long m10,long long m11)
{
Matrix2By2 *matrix = (Matrix2By2*)malloc(sizeof(Matrix2By2));
if (NULL == matrix)
exit(-1);
matrix->m00 = m00;
matrix->m01 = m01;
matrix->m10 = m10;
matrix->m11 = m11;
return matrix;
}
// 2個矩陣相乘,釋放內存時注意2個矩陣相同的情況
Matrix2By2 *MatrixMultiply(Matrix2By2 *matrix1,Matrix2By2 *matrix2)
{
Matrix2By2 *tmp = (Matrix2By2*)malloc(sizeof(Matrix2By2));
if (NULL == tmp)
exit(-1);
tmp->m00 = matrix1->m00 * matrix2->m00 + matrix1->m01 * matrix2->m10;
tmp->m01 = matrix1->m00 * matrix2->m01 + matrix1->m01 * matrix2->m11;
tmp->m10 = matrix1->m10 * matrix2->m00 + matrix1->m11 * matrix2->m10;
tmp->m11 = matrix1->m10 * matrix2->m01 + matrix1->m11 * matrix2->m11;
if (NULL != matrix1)
free(matrix1);
if (matrix1 != matrix2 && NULL != matrix2)
free(matrix2);
matrix1 = NULL;
matrix2 = NULL;
return tmp;
}
// 計算斐波那契結果
Matrix2By2 *MatrixPower(unsigned n)
{
if (n <= 0)
exit(-1);
Matrix2By2 *matrix;
if (n == 1)
{
matrix = InitMatrix(1,1,1,0);
}
else if (n%2 == 0)
{
matrix = MatrixPower(n/2);
matrix = MatrixMultiply(matrix, matrix);
}
else if (n%2 == 1)
{
matrix = MatrixPower((n-1)/2);
matrix = MatrixMultiply(matrix, matrix);
matrix = MatrixMultiply(matrix, InitMatrix(1,1,1,0));
}
return matrix;
}
// 矩陣法計算
long long offer74_fibonacci2(unsigned n)
{
if (n <= 1)
return n;
Matrix2By2 *tmp = MatrixPower(n - 1);
long long r = tmp->m00;
if (NULL != tmp)
free(tmp);
return r;
}
// 打印函數
void offer74_fibonacci_print(const char *topic, unsigned n, long long f(unsigned))
{
printf("%s : ", topic);
for (unsigned i = 0; i < n; ++i)
{
printf("%lld,", f(i));
}
printf("\n");
}
void main(void)
{
unsigned n = 20;
offer74_fibonacci_print("fibonacci0", n, offer74_fibonacci0);
offer74_fibonacci_print("fibonacci1", n, offer74_fibonacci1);
offer74_fibonacci_print("fibonacci2", n, offer74_fibonacci2);
}
2.總結
看了上述代碼可以發現,遞歸最簡潔,但是計算過程中會存在大量重復計算,如果求比較大的數時棧內存會消耗比較大。循環的方法是比較容易想到的,但是這種算法的時間復雜度是O(n),矩陣乘方的方式時間復雜度只有O(logN)。以上就是這3中方式的優缺點。