C语言中斐波拉契数列算法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言中斐波拉契数列算法相关的知识,希望对你有一定的参考价值。

#include<stdio.h>
long int f(long int x)

if(x==1)
return 1;
if(x==2)
return 1;
else
return f(x-1)+f(x-2);


int main()

int a,i;
scanf("%d",&a);
for(i=1;i<a;i++)

printf("%d,",f(i));

if(i=a)
printf("%d\n",f(i));
system("pause");
return 0;

按照我编的这个程序,运行的时候得到前十个数很容易,可是越往后面越慢,得出第20个数到第二十一个数已经需要很长时间(我的电脑还不错的说),我认为是因为···举个例子:计算F(100),它会计算F(99)+F(98),然而F(99)它又会计算F(98)+F(97),这样总共就要计算2^100次,所以时间很长很长。
现在我不想使用其他的函数,有什么方法可以改进的呢?是不是有某种方法能够使我得到的F(1),F(2),F(3)分一块内存存下来,要用到的时候可以直接读取而不是再三的计算得到呢?
static long int f(long int x) 这种定义不可以实现⊙﹏⊙b。真心求助各位大牛,怎么提高运算速度。

你每次计算f(i)都是从头开始的(每次都从f(1)开始一步一步的算),你在循环里调用f函数也就是说对每个i函数f都从i递归到1,做了很多重复功,这就很耗费时间,大大的浪费时间空间资源。其实完全可以不用函数的,i从1开始没计算一个值就保存并输出那个,这样就只需要一次循环,很简单的。还有long int型的有效位数是10位,就算是unsigned long int型的也是只能表示从0到4294967295,更本不可能到f(100),f(100)远超出long int 可表示的范围,long int只能到第46个(1836311903)。
下面是我写的程序,你可以看看(我保留你的f函数,不过没什么用):
#include<stdio.h>
int f(int x)

if(x==1)
return 1;
if(x==2)
return 1;


int main()

int a,i;
unsigned long num[101];
scanf("%d",&a);
for(i=1;i<=a;i++)

if(i==1||i==2)

num[i]=f(i);
printf("%d\n,",num[i]);

else

num[i]=num[i-1]+num[i-2];
printf("%ld\n",num[i]);



return 0;
参考技术A 递归 运算到后面计算量太大了
这个用递推的好!
#include <stdio.h>
void fa(int n)
int i,x=1,y=1;
for(i=1;i<=n;i++)
if(i<3)
printf("1\t");
else
y=x+y;printf("%d\t",y);
x=y-x;



void main()

int n;
printf("input a number:\n");
scanf("%d",&n);
fa(n);
printf("\n");
system("pause");
参考技术B 若只是想打印结果,可以这样写:
#include<stdio.h>
long int f(long int x)

if(x==1)
{printf("%d",1);
return 1;}
if(x==2)
{ printf("%d",1);
return 1;}
else
int m=f(x-1)+f(x-2);
printf("%d",m);
return m;}


int main()

int a,i;
scanf("%d",&a);
f(a);
system("pause");
return 0;
追问

好多语法报错了额·····我不知道怎么改了····
是不是有某种方法能够使我得到的F(1),F(2),F(3)分一块内存存下来,要用到的时候可以直接读取而不是再三的计算得到呢?
非常想知道这个问题有没有解决方法

追答

把int 声明改 为long int 看看。
可以记录f(N)的数值。需要修改一下函数

f(int n,int *a)//a为长度>=N的动态数组,传入f当参数时,须初始化每一行为0,完成程序后,a中就保存了对应的数值。

if(a[n]!=0)
return a[n] ;
else if(n==1||n==2)
a[n]=1;
return 1;
else
a[n]= f(n-1,a)+f(n-2,a);
return a[n] ;


参考技术C main()

  

  long fib[40] = 1,1;

  int i;

  for(i=2;i<40;i++)

  

  fib[i ] = fib[i-1]+fib[i-2];

  

  for(i=0;i<40;i++)

  

  printf("F%d==%d\n", i, fib[ i]);

  

  return 0;

  追问

输出的结果完全不对啊······⊙﹏⊙b汗

追答

//利用循环输出前40项
  #include
  int main()
  
  long fib[41] = 0,1;
  int i;
  for(i=2;i1) return fib(n-1)+fib(n-2);
  
  int main()
  
  int k=1;
  while(k!=0)
  
  puts("input a num n 0 for exit");
  scanf("%d",&k);
  printf("%ld\n",fib(k));
  
  getch();
  return 0;
  
  //高精度斐波那契数列(3000长度的可求到第10000多项)
  #include
  #include
  using namespace std;
  int a[3001]=;
  int b[3001]=;
  int c[3001]=;
  int n;
  int main()
  
  scanf("%d",&n);
  if(n=10)
  c[j]=c[j]-10;
  c[j+1]=1;
  
  
  int i=3000;
  while(c[i]==0) i--;
  for(i=i;i>=1;i--)
  printf("%d",c[i]);
  printf("\n");
  system("pause");
  return 0;
  

本回答被提问者采纳
参考技术D haha

动态规划-斐波拉契数列笔记

14天阅读挑战赛

目录

简介

Hello!
非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~
 
ଘ(੭ˊᵕˋ)੭
昵称:海轰
标签:程序猿|C++选手|学生
简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金,有幸在竞赛中拿过一些国奖、省奖…已保研
学习经验:扎实基础 + 多做笔记 + 多敲代码 + 多思考 + 学好英语!
 
唯有努力💪

算法知识点


算法题目来源

来源:leetcode.cn/

算法题目描述

斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

给定 N,计算 F(N)。

示例 1:

输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1.

示例 2:

输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2.

示例 3:

输入:4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3.

提示:

  • 0 ≤ N ≤ 30

做题思路

方法一:递归

从题目中可以看出,f(0)=0,f(1)=1,所以N<=1时,f(N)=N;N>1时,f(N)=f(N-1)+f(N-2)
那么可以很快写出下面的代码:

class Solution 
public:
    int fib(int n) 
        if(n <= 1) return n;
        return fib(n-1) + fib(n-2);
    
;

运行结果

上面代码是一个基础的递归写法。对于递归,海轰有这种感受:看到答案后,这题太简单了吧;只看题目,emm,太难,大概知道方法是啥,但就是写不出代码!
初学递归时,最难的就是无法知道程序具体是如何运行的?每一步干啥了?
笨鸟先飞!既然咱们的大脑想不出,那就动手模拟程序的运行过程,想象自己就是计算机,运行一次代码。这里我们假设需要计算f(4),得到如下的程序运行图:

从图中我们可以发现:首先进入fib函数时,N=4,发现不满足N<=1的条件,那么就运行return 语句,而运行retun fib(3)+fib(2),首先需要计算fib(3),那么进而转入下一个fib函数,此时N=3,发现不满足N<=1,那么就运行return, 而运行return f(2)+f(1),又需要计算f(2),那么又转了下一个fib函数,此时N=2…
按照上图的路径跑一下f(4)是如何计算出的,那么对于f(N)的一般过程我们也就可以懂了。开始的时候觉得这样画图很浪费时间,没有必要,但是每次遇到递归时,都需要思考很久,因为不知道具体程序如何运行,特别是二叉树那里!多画了几次后,渐渐也懂了递归的思想,简单题慢慢也可以做出了。

方法二:利用辅助数组

将上面的图简化,得到下图。

我们可以发现使用方法一,我们做了非常多的无用功:比如计算f(4)时,我们需要算出f(2),而在计算f(3)时,我们又需要计算f(2)…当N非常大的时候,这样的重复计算就会浪费非常多的时间。
那么有什么办法可以解决这个问题呢?
假设第一次计算出f(2)后,我们将它保存在数组中,下一次需要用到f(2)时,我们直接从数组中提取。这样一来,每个f(n)就只计算了一次,大大提高了运行效率。
编写代码如下:(递归+全局辅助数组)

// 定义全局辅助数组,因为由题目已知最大N=30,那么数组长度最多为31,初始化都为-1,
// 计算出一个f值,填入对应数组中的一个元素 
// 每次先测试对应数组中的元素,如果为-1,表示尚未计算,需要进行第一次的计算
// 如果不为-1 表示已经计算过了 直接提取即可
// 假设我们需要f(10),首先看看a[10]是否为-1,如果a[10]=-1,则需要计算f(10)
// 反之 直接提取a[10]中的值即可
vector<int> a= vector<int> (31,-1);
int fib(int N)

    if (N <= 1)
        return N;
    // 如果已经计算过 则直接返回存在数组中的值
    if (a[N] != -1)
        return a[N];
    // 若没有 则需要计算出 并保存在相应的位置
    a[N] = fib(N - 1) + fib(N - 2);
    return a[N];

运行结果

在上面的代码中,海轰使用了全局辅助数组来存储f(n)。不知道细心的小伙伴发现没有,数组a的长度这里是直接采用的N的最大值+1。题目中N<=30,但假设N=10000时,我们每次都生成长度为10001长度的数组,那么在计算f(3)、f(10)…的时候,会造成非常大的空间浪费。
优化方案:动态分配,根据N的大小,每次固定分配N+1的空间(生成长度为N+1的数组)
改进后编写代码如下:(递归+辅助数组)

int help(vector<int> &a,int N)
    if(N<=1) return N;
    if(a[N]!=-1) return a[N];
    a[N]=help(a,N-1)+help(a,N-2);
    return a[N];

int fib(int N)

    vector<int> a(N+1,-1);//每次生成数组的长度永远都是N+1 动态变化
    return help(a,N);

运行结果

方法三:动态规划

在方法二中,我们可以发现,计算f(20)时,需要先计算f(19)【注:f(20)=f(19)+f(18),代码中是return f(n-1)+f(n-2),所以会需要先计算f(19),f(19)计算完成后,再计算f(18)】;计算f(19)时,需要先计算f(18)…可以发现程序计算的顺序是:20->19->18->17… 从顶向下的。
那么是否可以反过来?从底向上呢?答案是:可以的。
通过表达式f(N)=f(N-1)+f(N-2)可得,一个表达式的值是由前面两个表达式决定的【f(0)、f(1)除外】。假设我们求f(4),由已知:f(0)=0 f(1)=1, 可以求得 f(2)=f(1)+f(0)=1+0=1 f(3)=f(2)+f(1)=1+1=2 f(4)=f(3)+f(2)=3。这样不就是从底向上求得结果了吗?【哈哈,其实仔细想想,这不就是我们平时自己的思路吗】
那么如何模拟这种思路呢?这里我们依然借助一个数组a,长度为N+1【为啥长度是N+1?假设N=4,那么我们需要计算f(0)、f(1)、f(2)、f(3)、f(4),需要长度为5的数组存储每一个f(n)】
首先f(0)=0 f(1)=1是已知的,也就有:a[0]=1 a[1]=1,然后依次计算f(2)、f(3)…
以f(2)计算为例:
f(2)=f(1)+f(0)=1+0=1
计算出后,立马存入a[2]:a[2]=1
计算f(3)时 如果用f(3)=f(2)+f(1),这里f(2)是未知的哦
但是还记得我们在数组中存储了f(2)吧 也就是a[2]
所以
f(3)=a[2]+a[1]
依次类推 得出规律
a[n]=a[n-1]+a[n-2] // 需要求f(n) ,仔细想想,是不是就是求a[n]

编写代码如下:(自底向上)

class Solution 
public:
int fib(int N)

    // N<2 直接返回N
    if(N<=1) return N;
    
    // N>=2时
    // 利用一个辅助数组,存下N对应于数组中的值
    // 比如 算出f(2)=1后,令a[2]=1 便于后面的运算
    vector<int> a(N+1,0);
    a[0]=0;
    a[1]=1;
    for(int i=2;i<=N;++i)
        a[i]=a[i-1]+a[i-2];
    
    return a[N];

;

运行结果

思考:
不知道有小伙伴发现没有,其实每次参与运算的变量其实就两个:一个f(n),一个f(n-1),以f(7)为例:f(2)=f(1)+f(0)–>f(3)=f(2)+f(1)–>f(4)=f(3)+f(2)… f(7)=f(6)+f(5)
优化:
我们可以使用两个变量P1、P2,其中P1是左边较小,P2是右边较大的。
初始化:P1=0 P2=1
两个指针分别更新:P2=P2+P1 P1=P2(这里的P2是指原来的P2=1,不是与P1相加后的P2,所以需要一个临时变量存储原来的P2)
移动规则:

temp=P2
P2=P2+P1;
P1=temp;

编写代码如下:

int fib(int N)

    if(N<=1) return N;
    int p1=0;
    int p2=1;
    for(int i=2;i<=N;++i)
        int temp=p2;
        p2+=p1;
        p1=temp;
    
    return p2;

运行结果

注:优化后性能确实会提升,上图仅为某一次运行截图

方法四:矩阵求幂

由数学可得:斐波那契数列矩阵方程

方程解释如下:

编写代码如下:

int fib(int N)

    if(N<=1) return N;
    int A[2][2]=
        1,1,
        1,0;
    martixpower(A,N-1);// 求 A矩阵的n-1次方
    return A[0][0];// 从上面公式证明可以看出,答案就是n-1方中结果的[0][0]项

// 计算矩阵A的n-1次方
void martixpower(int A[2][2],int N)
    int B[2][2]=
        1,1,
        1,0
    ;
    // 利用循环 模拟n-1次方
   for(int i=0;i<N-1;++i)
       multiply(A,B);
   

// 矩阵乘法 A*B
void multiply(int A[2][2],int B[2][2])
        int x = A[0][0] * B[0][0] + A[0][1] * B[1][0];
        int y = A[0][0] * B[0][1] + A[0][1] * B[1][1];
        int z = A[1][0] * B[0][0] + A[1][1] * B[1][0];
        int w = A[1][0] * B[0][1] + A[1][1] * B[1][1];

        A[0][0] = x;
        A[0][1] = y;
        A[1][0] = z;
        A[1][1] = w; 

运行结果

思考:
假设我们需要求 3 100 3^100 3100=?
使用for循环:333…33 ,那么将会做99次乘法

但从另一个角度思考:
3 100 3^100 3100= 3 50 3^50 350 3 50 3^50 350 :如果知道 3 50 3^50 350 那么再与自身相乘就可以得 3 100 3^100 3100,需要一次乘法
3 50 3^50 350= 3 25 3^25 325
3 25 3^25 325
通过上面我们可以发现
3 1 3^1 31–> 3 2 3^2 32–> 3 4 3^4 34–> 3 8 3^8 38–> 3 16 3^16 316–> 3 32 3^32 332
总结:通过1次方可以求出2次方,2次方可以求出4次方,4次方可以求出8次方…

所以,利用递归,求A的N-1次方代码如下:(这里求矩阵的N-1次方也用到了递归哦 小伙伴可以试试 会有收获的)

int fib(int N)

    if(N<=1) return N;
    int A[2][2]=
        1,1,
        1,0;
    martixpower(A,N-1);// 求 A矩阵的n-1次方
    return A[0][0];

// 递归求N-1次方
void martixpower(int A[2][2],int N)
    if(N<=1) return;
    martixpower(A,N/2);// 求A矩阵的(n-1)/2 次方
    multiply(A,A); 求的(n-2)/次方后 再与自身相乘
    // 如果N是奇数 则还需要在乘一次B 
    if(N%2==1)
        int B[2][2]=
            1,1,
            1,0
        ;
        multiply(A,B);
    

void multiply(int A[2][2],int B[2][2])
    int x = A[0][0] * B[0][0] + A[0][1] * B[1][0];
        int y = A[0][0] * B[0][1] + A[0][1] * B[1][1];
        int z = A[1][0] * B[0][0] + A[1][1] * B[1][0];
        int w = A[1][0] * B[0][1] + A[1][1] * B[1][1];

        A[0][0] = x;
        A[0][1] = y;
        A[1][0] = z;
        A[1][1] = w; 

测试结果

方法五:公

以上是关于C语言中斐波拉契数列算法的主要内容,如果未能解决你的问题,请参考以下文章

c语言编写计算斐波那契(Fibonacci)

剑指offer系列——7.斐波拉契数列

求解一个数变为斐波拉契数需要多少步(C语言)

斐波拉契

关于斐波拉契数列所引伸出来的问题

求数列1,1,2,3,5……前20项的和,用C语言编写