数列:1,1,2,3,5,8,13,21,34,55…….,被称为斐波那契数列。
斐波那契数列特点:第一、第二个数为1
,从第三个数开始,该值等于其前面两个数之和。
本文主要解决计算第N个斐波那契数的值。
1. 递归
/**
* 斐波那契数列
* @param [int] $n [数列第n个数,由0开始算]
* @return [int] [数列第n个数的值]
*/
function fB($n)
{
if ($n == 0 || $n == 1) {
return 1;
}
if ($n > 1) {
return fB($n - 1) + fB($n - 2);
}
}
递归法虽简单,但在递归时没有重复利用已经计算出来的子值,其时间复杂度较高。
2. 数组方式
/**
* 斐波那契数列
* @param [int] $n [数列第n个数,由0开始算]
* @return [int] [数列第n个数的值]
*/
function fB($n)
{
$arr = [1, 1];
if ($n == 0 || $n == 1) {
return 1;
}
if ($n > 1) {
for ($i = 2; $i <= $n; $i++) {
$arr[$i] = $arr[$i - 1] + $arr[$i - 2];
}
return $arr[$n];
}
}
当n的值比较大时,法2中的$arr数组随之也较大,空间复杂度较高。
通过分析斐波那契数列我们知道,当n大于等于2时,其值只与其前面两个数的值有关,所以在只需求出第n个值的时候,我们没必要浪费空间去存储在n前2个数之前的值。
法2 优化如下:
/**
* 斐波那契数列
* @param [int] $n [数列第n个数,由0开始算]
* @return [int] [数列第n个数的值]
*/
function fB($n)
{
$arr = [1, 1];
if ($n == 0 || $n == 1) {
return 1;
}
if ($n > 1) {
for ($i = 2; $i <= $n; $i++) {
$temp = $arr[0] + $arr[1];
$arr[0] = $arr[1];
$arr[1] = $temp;
}
return $arr[1];
}
}
$arr 数组只保存第n-1和第n个数的值。
拓展:当n比较大时,如何获得第n个斐波那契数的值?(该值超过了计算机整型数的存储范围)
解法思路如下:采用两个数组存储第n-1和第n个数的值,每个数组的每个元素只存储0-9
之间数,拆分倒叙存储,如13的存储形式为:[3,1],对这两个数组进行对应位置相加,满十进一,即可得出第n个数的倒叙数组,代码如下:
/**
* 斐波那契数列(支持大数值)
* @param [int] $n [数列第n个数,由0开始算]
* @return [int] [数列第n个数的值]
*/
function fB($n)
{
$arr1 = [1];
$arr2 = [1];
if ($n == 0 || $n == 1) {
return 1;
}
if ($n > 1) {
for ($i = 2; $i <= $n; $i++) {
$arr3 = [];
$to = 0; // 进位
$total1 = count($arr1);
$total2 = count($arr2);
for ($j = 0; $j < $total1; $j++) {
$sum = $arr1[$j] + $arr2[$j] + $to;
$arr3[$j] = $sum % 10;
$to = (int)($sum / 10);
}
if ($to > 0) { // 进位
$arr3[$j] = $to;
}
for (; $j < $total2; $j++) {
$arr3[$j] = $arr2[$j] + $to;
$to = 0;
}
$arr1 = $arr2;
$arr2 = $arr3;
}
$total2 = count($arr2);
$result = '';
for ($i = 0; $i < $total2; $i++) {
$result = $arr2[$i] . $result;
}
return $result;
}
}