Fibonacci 第 n 项及Fibonacci 前 n 项和

这其实是两道题目,但是他们量都题目的思路是在是相像,而且炒鸡厉害。

Fibonacci 第 n 项

题目描述
大家都知道 Fibonacci 数列吧,f1=1,f2=1,f3=2,f=3...fn=fn-1+fn-2,
f1=1,f2=1,f3=2,f4=3,…,fn=fn?1+fn?2。
现在问题很简单,输入 n 和 m,求 fn mod m

输入格式
输入 n,m。
输出格式
输出 fn mod m

样例输入
5 1000
样例输出
5

Fibonacci 前 n 项和

题目描述
大家都知道 Fibonacci 数列吧,f1=1,f2=1,f3=2,f=3...fn=fn-1+fn-2,
f1=1,f2=1,f3=2,f4=3,…,fn=fn?1+fn?2。
现在问题很简单,输入 n 和 m,求 {fn}的前n项sn mod m

输入格式
输入 n,m。
输出格式
输出 fn mod m

样例输入
5 1000
样例输出
12

这两个的数据都是很惊人,都是$$n<=2*10^{9}$$
有木有一种恐怖袭击的来临,很显然我们不能用传统的思想来,因为数组根本存不下,要不然我写这个有什么意思
所以这里就要用到矩阵乘法
切入正题,核核。首先是Fibonacci 第 n 项这道题,我们可以知道:

f[i]=1*f[i-1]+1*f[i-2]
f[i-1]=1*f[i-1]+0*f[i-2]

把他们看成一个矩阵,转换为
$$
\begin{pmatrix}
f[i] \
f[i-1]
\end{pmatrix}
=\begin{pmatrix}
1 & 1 \
1 & 0
\end{pmatrix}*
\begin{pmatrix}
f[i-1] \
f[i-2]
\end{pmatrix}
$$
因为

f[i]=1*f[i-1]+1*f[i-2]
f[i-1]=1*f[i-1]+0*f[i-2]

所以可以根据矩阵乘法(我默认你矩阵乘法懂了 )得出刚刚的那个矩阵
接着我们又可以得到:
$$
\begin{pmatrix}
f[i] \
f[i-1]
\end{pmatrix}
=\begin{pmatrix}
1 & 1 \
1 & 0
\end{pmatrix}
\begin{pmatrix}
1 & 1 \
1 & 0
\end{pmatrix}

\begin{pmatrix}
f[i-2] \
f[i-3]
\end{pmatrix}
$$
然后这样一直化简,可以得出一般式
$$
\begin{pmatrix}
f[i] \
f[i-1]
\end{pmatrix}
=\begin{pmatrix}
1 & 1 \
1 & 0
\end{pmatrix}^{n-2}
\begin{pmatrix}
f[2] \
f[1]
\end{pmatrix}
$$
然后因为矩阵可以用乘法结合律,所以我们可以用快速幂,把
$$
\begin{pmatrix}
1 & 1 \
1 & 0
\end{pmatrix}
$$
看成是一个数x,那么我们就可以用快速幂去求$$x^{n-2}$$的值啦。大大减少了空间有木有【0_0】

然后上代码(我都说的这么清楚了还用得着看代码?!

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef signed long long ll;
struct node
{
    ll p[3][3];
    node(){memset(p,0,sizeof(p));}
};
int n;
ll M;
node A;
node plus(node a,node b)
{
    node c;
    for(int i=1;i<=2;i++)
        for(int j=1;j<=2;j++)
            for(int k=1;k<=2;k++)
                c.p[i][j]+=a.p[i][k]*b.p[k][j],c.p[i][j]%=M;
    return c;
}
node plus1(node a,node b,int n,int m,int p)
{
    node c;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=p;j++)
            for(int k=1;k<=m;k++)
                c.p[i][j]+=a.p[i][k]*b.p[k][j],c.p[i][j]%=M;
    return c;
}
node mi(int n)//快速幂运算,也可以用while,因为这个有可能会爆栈
{
    if(n==1)return A;
    node t=mi(n/2);
    if(n%2==1)return plus(plus(t,t),A);
    else return plus(t,t);
}
int main()
{
    A.p[1][1]=1;A.p[1][2]=1;
    A.p[2][1]=1;A.p[2][2]=0;
    scanf("%d %llu",&n,&M);
    node c=mi(n-2),ha;//矩阵的幂运算
    ha.p[1][1]=1;ha.p[2][1]=1;//因为f[1]=f[2]=1,这是题目给出的
    c=plus1(c,ha,2,2,1);
    printf("%llu\n",c.p[1][1]%M);
    return 0;
}

然后下一题跟这一题目的思路很像,因为题目说s[i]={f[i]}前i项的和,所以可以得出:

//这里我们用s[n-1],f[n],f[n-1]来表示这s[n],f[n+1],f[n]
s[n]=1*s[n-1]+1*f[n]+0*f[n-1]
//因为s[n]是前n个肥波数列的前缀和,所以他就是前n-1的前缀和加上第n个肥波数列
f[n+1]=0*s[n-1]+1*f[n]+1*f[n-1]
//那第n项的下一个就是f[n]+f[n-1]
f[n]=0*s[n-1]+1*f[n]+0*f[n-1]
//f[n]就为f[n]
//这样我们就建立起了关系

这种思路很好理解,但很难想哎!
然后像上一题一样把他换成矩阵,就得
$$
\begin{pmatrix}
s[n] \
f[n+1] \
f[n]
\end{pmatrix}=
\begin{pmatrix}
1 & 1 & 0 \
0 & 1 & 1 \
0 & 1 & 0
\end{pmatrix}*
\begin{pmatrix}
s[n-1] \
f[n] \
f[n-1]
\end{pmatrix}
$$
然后可以一直化简到一般式
$$
\begin{pmatrix}
s[n] \
f[n+1] \
f[n]
\end{pmatrix}=
\begin{pmatrix}
1 & 1 & 0 \
0 & 1 & 1 \
0 & 1 & 0
\end{pmatrix}^{n-1}
\begin{pmatrix}
s[1] \
f[2] \
f[1]
\end{pmatrix}
$$
一样的思路,上代码!

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef signed long long ll;
struct node
{
    ll p[4][4];
    node(){memset(p,0,sizeof(p));}
};
int n;
ll M;
node A;
node plus(node a,node b)
{
    node c;
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            for(int k=1;k<=3;k++)
                c.p[i][j]+=a.p[i][k]*b.p[k][j],c.p[i][j]%=M;
    return c;
}
node plus1(node a,node b,int n,int m,int p)
{
    node c;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=p;j++)
            for(int k=1;k<=m;k++)
                c.p[i][j]+=a.p[i][k]*b.p[k][j],c.p[i][j]%=M;
    return c;
}
node mi(int n)
{
    if(n==1)return A;
    node t=mi(n/2);
    if(n%2==1)return plus(plus(t,t),A);
    else return plus(t,t);
}
int main()
{
    A.p[1][1]=1;A.p[1][2]=1;A.p[1][3]=0;
    A.p[2][1]=0;A.p[2][2]=1;A.p[2][3]=1;
    A.p[3][1]=0;A.p[3][2]=1;A.p[3][3]=0;
    scanf("%d %llu",&n,&M);
    node c=mi(n-1),ha;
    ha.p[1][1]=1;ha.p[2][1]=1;ha.p[3][1]=1;
    c=plus1(c,ha,3,3,1);
    printf("%llu\n",c.p[1][1]%M);
    return 0;
}

总结:矩阵乘法大大压缩了空间

本文思路摘自《一本通提高篇》

原文地址:https://www.cnblogs.com/candy067/p/11401960.html

时间: 2024-11-02 00:08:14

Fibonacci 第 n 项及Fibonacci 前 n 项和的相关文章

常系数线性递推的第n项及前n项和 (Fibonacci数列,矩阵)

(一)Fibonacci数列f[n]=f[n-1]+f[n-2],f[1]=f[2]=1的第n项的快速求法(不考虑高精度). 解法: 考虑1×2的矩阵[f[n-2],f[n-1]].根据fibonacci数列的递推关系,我们希望通过乘以一个2×2的矩阵,得到矩阵[f[n-1],f[n]]=[f[n-1],f[n-1]+f[n-2]] 很容易构造出这个2×2矩阵A,即: 0 1 1 1 所以,有[f[1],f[2]]×A=[f[2],f[3]] 又因为矩阵乘法满足结合律,故有: [f[1],f[2

Fibonacci前n项和 (矩阵乘)

大家知道Fibonacci数列吧, f[1]=1, f[2]=1, f[3]=2, f[4]=3…, 也就是f[n]=f[n-1]+f[n-2],现在问题很简单,输入n和m,求前n项和取模m. #include <iostream> #include <algorithm> #include <cstdio> #include <string> #include <cstring> #include <cstdlib> #includ

Python初学者笔记:打印出斐波那契数列的前10项

问题:斐波那契数列(意大利语: Successione di Fibonacci),又称黄金分割数列.费波那西数列.费波拿契数.费氏数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.--在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*),用文字来说,就是斐波那契数列列由 0 和 1 开始,之后的斐波那契数列系数就由之前的两数相加.特别指出:0不是第一项,而是第零项. 方法:Python2.7.9 a=0 b=

求f(k)=k^k(k=1...n)的前n项和

求f(k)=k^k(k=1...n)的前n项和. 程序实现: #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> long long My_Mul_Sum(int *n)//封装了一个求k^k的前n项和的函数 { int k = 1; long long sum = 0;//定义为long long是为了防止数据较大,容易溢出 for (k = 1; k <= n; k++) { int count = 0, mul = 1;//count

2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。

package com.hanqi; public class Qiuhe { public static void main(String[] args) { // TODO 自动生成的方法存根 double sum=0,fenshu=0,a=1,b=2,c=0; for (int i = 0; i < 20; i++) { fenshu=b/a; c=a; a=b; b=a+c; sum+=fenshu; //System.out.println(fenshu); } System.out.

循环-26. 求给定序列前N项和之四

循环-26. 求给定序列前N项和之四(15) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 来源 ZOJ 本题要求编写程序,计算序列 1 - 1/4 + 1/7 - 1/10 + ... 的前N项之和. 输入格式: 输入在一行中给出一个正整数N. 输出格式: 在一行中按照“sum = S”的格式输出部分和的值S,精确到小数点后3位.题目保证计算结果不超过双精度范围. 输入样例: 10 输出样例: sum = 0.819 1 #incl

循环-21. 求交错序列前N项和

1 /* 2 * Main.c 3 * C21-循环-21. 求交错序列前N项和 4 * Created on: 2014年8月18日 5 * Author: Boomkeeper 6 ***********测试通过******** 7 */ 8 9 #include <stdio.h> 10 11 int main(void){ 12 13 float numerator = 1; 14 float denominator = 1; 15 16 int N = 0;//前N项和 17 flo

有一分数序列: 2/1 3/2 5/3 8/5 13/8 21/13...... 求出这个数列的前N项之和,保留两位小数。

题目描述 输入 N 输出 数列前N项和 样例输入 10 样例输出 16.48 代码:#include<stdio.h>int main(){    int i,N;    double c=0,a=2.0,b=1.0,t,sum=0;         scanf("%d",&N);    for(i=0;i<N;i++)    {           c=a/b;        sum=sum+c;        t=a;        a=a+b;     

码农谷 求前N项之和

题目描述 有一分数序列:2/1.3/2.5/3.8/5.13/8.21/13.......求出这个数列的前N项之和,保留两位小数. 输入描述 N 输出描述 数列前N项和 样例 输入: 10 输出: 16.48 第一次code: 使用递归: 1 import java.util.*; 2 import java.math.BigDecimal; 3 4 public class jisuanrong 5 { 6 public static double SimpleAdding(int num)