类似斐波那契数列的递归

02-类似斐波那契数列的递归

斐波那契数列的递归和线性实现方式

//1.递推实现
public static int recurGetFibonaccN(int N) {
    if (N == 1 || N == 2) return 1;
    if (N == 3) return 2;
    return recurGetFibonaccN(N - 1) + recurGetFibonaccN(N - 2);
}

//2.线性方式实现
public static int getFibonaccN(int N) {
    if (N == 1 || N == 2) return 1;
    if (N == 3) return 2;
    int resN_2 = 1;
    int resN_1 = 2;
    for (int i = 4; i <= N; i++) {
        int temp = resN_1;
        resN_1 += resN_2;
        resN_2 = temp;
    }
    return resN_1;
}

按照上面两种实现方式最多只能将时间复杂度优化到O(N),而通过线性代数的一种定理可以将斐波那契问题优化到log(N)
定理:如果F(N) = aF(N-1)+bF(N-2)+...+cF(n-k)这样的递推式是不以条件转移的(就是不是在某种条件下才成立,而是强成立的)那么就有这么一种结论:|F(N),F(N-1)...,F(N-k+1)| = |F(k),F(k-1)...,F(1)|*(k阶矩阵行列式)^(n-k)

这个n-k是这么推断出来的,咱们先求斐波那契问题,斐波那契是2阶问题,就是每个N位置和前面两个位置有关的意思。而它通过以下的式子可以得出最后乘以2阶矩阵的n-2次方

image-20210310223047112.png
//利用这个定理解决斐波那契问题F(N) = F(N-1)+F(N-2)这是定理的二阶问题,我们需要求解一个2x2的矩阵行列式
//{
//  {a,b},
//  {c,d},列出两个式子求解出这四个变量就解出了行列式,几阶问题就需要找几个例子来计算行列式
//}
//解得
//{
//  {1,1},
//  {1,0}
//}
//所以|F(N),F(N-1)|=|F(2),F(1)|* |1,1|^(N-2)
//                              |1,0|

//现在问题就转变成了如何快速求解一个矩阵的N次幂,有一种快速幂算法
//比如求6的75次方,最快的方式是使用快速幂算法
//75-->toBinary==> 1001011
//6^75 => 6^64*6^8*6^2*6^1
//也就是说遍历这个二进制串,整个中间变量t,每次都乘以6,整出6^1,6^2这样的中间值
//如果此时遍历到的位的值是1就将这个中间变量的值加到res中,最后返回res不就是幂值嘛,实现一把

//return a^b b是大于大于0的整数
// 6^75 => 6^64*6^8*6^2*6^1
public static int quickPow(int a, int b) {
    int t = a;
    int res = 1;
    do {
        if ((b & 1) != 0) {//看一下最后一位是否为1,表示此时需不需要将中间值乘到结果中
            res *= t;
        }
        t *= t;//拿2的幂来说,一开始是2^1,然后2^2,2^4,2^8这样,所以每次都是乘以自己
    } while ((b >>= 1) != 0);
    return res;
}

//同理,我们现在想要算矩阵的快速幂,那么就只需要知道两个矩阵怎么乘能够拿到中间值不就可以了?
//if a,b is valid,return a*b
public static int[][] matrixMul(int[][] a, int[][] b) {
    if (a == null || b == null) return null;
    if (a[0].length != b.length) return null;
    int[][] res = new int[a.length][b[0].length];
    for (int i = 0; i < res.length; i++) {
        for (int j = 0; j < res[0].length; j++) {
            //res[i][j]需要a的i行乘以b的[j]列的和
            for(int ik = 0;ik<a[0].length;ik++){//ik表示i行k列
                res[i][j]+=a[i][ik]*b[ik][j];//每个i行k列都要乘以b中k行j列
            }
        }
    }
    return res;
}
//矩阵的快速幂算法 return a^b
public static int[][] matrixPow(int[][] a,int b){
    int[][] res = new int[a.length][a[0].length];
    //res初始化为一个单位矩阵,单位矩阵就表示矩阵中的1
    for(int i = 0;i<res.length;i++){
        res[i][i] = 1;
    }
    int[][] t = a;
    do{
        if((b&1)!=0){
            res = matrixMul(res,t);
        }
        t = matrixMul(t,t);
    }while ((b>>=1)!=0);

    return res;
}
public static int quickGetFibonacciN(int N){
    if(N<1) return 0;
    if(N==1||N==2) return 1;
    int[][] base = {
            {1,1},
            {1,0}
    };
    //|F(N),F(N-1)|=|1,1|*base^(N-2)
    //base^(N-2) = {{x,y},{z,d}} ==> F(N) = x+z
    int[][] basePow = matrixPow(base, N - 2);
    return basePow[0][0]+basePow[1][0];
}

小试牛刀

例题一:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

//思路:这里先进行推理:
//1.  一个台阶只有一种爬法
//2.  两个台阶有两种爬法
//3.  三个台阶等于先爬到第一个台阶然后直接爬两步到第三级台阶加上先爬到第二个台阶然后直接爬异步到第三级台阶的和
//    那就是1+2=3
//         ....
//n. F(N) = F(N-1)+F(N-2),这就是和斐波那契数列一样的二阶问题
public static int getUpStairNum(int N){
    if(N<1) return 0;
    if(N<=2) return N==1?1:2;
    int[][] base = {
            {1,1},
            {1,0}
    };
    int[][] matrixPow = matrixPow(base, N - 2);
    return 2*matrixPow[0][0] + matrixPow[1][0];
}

例题二:

//例题2:生奶牛问题
/*
第一年农场有1只成熟的母牛A,往后的每年:
1)每一只成熟的母牛都会生一只母牛
2)每一只新出生的母牛都在出生的第三年成熟
3)每一只母牛永远不会死
返回N年后牛的数量
*/
/*
思路:先进行推理
第一年:A(成熟)-->1个
第二年:A(成熟),B-->2个
第三年:A(成熟),B,C-->3个
第四年:A(成熟),B(成熟),C,D-->4个
第五年:A(成熟),B(成熟),C(成熟),D,E,F-->6个
第六年:A(成熟),B(成熟),C(成熟),D(成熟),E,F,G,H,I-->9个
经过推演,可以发现第N年的牛的个数F(N)=F(N-1)+0*F(N-2)+F(N-3)
就是去年出生的今年还会活着,而三年前就已经出生的,现在肯定是成熟的,可以生新的小牛
|F(N),F(N-1),F(N-2)| = |F(3),F(2),F(1)|*|3*3|^(N-3)
这是一个三阶问题
我发现在计算base矩阵的时候,那些个矩阵中的值不是0就是1,有时候可以合理猜测节省解方程时间
*/
public static int getCowNum(int N){
    if(N<1) return 0;
    if(N<4) return N==1?1:(N==2?2:3);
    int[][] base = {
            {1,1,0},
            {0,0,1},
            {1,0,0}
    };
    int[][] matrixPow = matrixPow(base, N - 3);
    return 3*matrixPow[0][0]+2*matrixPow[1][0]+matrixPow[2][0];
}

例题三:

//例题3:合法01串问题
/*
给定一个数N,想象只由0和1两种字符,组成的所有长度为N的字符串
如果某个字符串,任何0字符的左边都有1紧挨着,认为这个字符串达标
返回有多少达标的字符串

思路:字符串的开头肯定1,这是确定的,然后第二位可以选择是0或者1,第三位可以在第二位选0的时候选1
也就是说我们可以设置递归思路这样,当前位选了1,然后返回有多少达标的字符串
*/
//返回第一位是1时,后面N-1个字符串能够排列出合法字符串的个数
//可以多列几项,看看这一项可不可以由前几项加工出来
public static int recurGetValidStrNum(int N){
    if(N<1) return 0;
    if(N<=2) return N==1?1:2;
    return recurGetValidStrNum(N-1)+recurGetValidStrNum(N-2);
}
public static int quickGetValidStrNum(int N){
    if(N<1) return 0;
    if(N<=2) return N==1?1:2;
    int[][] base = {
            {1,1},
            {1,0}
    };
    int[][] matrixPow = matrixPow(base, N - 2);
    return 2*matrixPow[0][0] + matrixPow[1][0];
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容