数据结构与算法学习笔记(训练营一第二节)---菲波那切数列

斐波那契数列

  • 求斐波那契数列矩阵乘法的方法
    1)斐波那契数列的线性求解(O(N))的方式非常好理解;
    2)同时利用线性代数,也可以改写出另一种表示:
    | F(N) , F(N-1) | = | F(2), F(1) | * 某个二阶矩阵的N-2次方;
    3)求出这个二阶矩阵,进而最快求出这个二阶矩阵的N-2次方;
  • 类似斐波那契数列的递归优化
    如果某个递归,除了初始项之外,具有如下的形式
    F(N) = C1 * F(N) + C2 * F(N-1) + … + Ck * F(N-k) ( C1…Ck 和k都是常数)并且这个递归的表达式是严格的、不随条件转移的。

那么都存在类似斐波那契数列的优化,时间复杂度都能优化成O(logN)

  • 斐波那契数列可用递归,动态规划,求解做到O(logn),但是用快速幂可以做到O(logn)。
    1.快速幂:
    比如求a^75次方,一般方法a连续相乘75次,这样就太慢了。我们可以这样计算,幂指数75的二进制形式是1001011,定义一个变量res = 1,一个t变量 t= a1,从如果此时幂指数二进制位最右边是1那么把t和res相乘,如果不是一那么不进行相乘计算,且此时让t和t相乘得到第二轮的t,如此往复当幂指数为0时就得到了a75次方。
    2.斐波拉切数列的递推式:|F(n).F(n-1)=|F(2).F(1)||矩阵|^n-2
    3.推广:F(n) = C1
    F(n) +C2F(n-1) +...+CzF(n-k) C1、C2、Cz..和k都是常数,则都有O(logn)的解。
public class Fbo {

    // 递归
    public static int f(int n){
        if(n <= 0){
            return 0;
        }

        if(n <= 2){
            return 1;
        }

        return f(n - 1) + f(n - 2);
    }

    // 动态规划
    public static int dp(int n){
        if(n <= 0){
            return 0;
        }

        if(n <= 2){
            return 1;
        }

        int one = 1;
        int two = 1;
        int cur = 0;
        for (int i = 2; i < n; i++) {
            cur = one + two;
            one = two;
            two = cur;
        }
        return cur;
    }

    // 快速幂方法
    // 有线性代数德 => |Fnfn-1| = |F2F1|*(矩阵)^n-k(k=2)
    public static int quickM(int n){
        if(n <= 0){
            return 0;
        }
        if(n <= 2){
            return 1;
        }
        int[][] matrix = new int[][]{{1,1},{1,0}};
        int[][] ints = matrixPower(matrix, n - 2);

        return ints[0][0] + ints[1][0];

    }

    // 求矩阵的n次幂
    public static int[][] matrixPower(int[][] m, int p) {
        int[][] res = new int[m.length][m[0].length];
        for (int i = 0; i < res.length; i++) {
            res[i][i] = 1;
        }

        // res = 矩阵中的1
        int[][] tmp = m;// 矩阵1次方
        for (; p != 0; p >>= 1) {
            if ((p & 1) != 0) {
                res = muliMatrix(res, tmp);
            }
            tmp = muliMatrix(tmp, tmp);
        }
        return res;
    }

    // 两个矩阵相乘
    public static int[][] muliMatrix(int[][] m1, int[][] m2) {
        int[][] res = new int[m1.length][m2[0].length];
        for (int i = 0; i < m1.length; i++) {
            for (int j = 0; j < m2[0].length; j++) {
                for (int k = 0; k < m2.length; k++) {
                    res[i][j] += m1[i][k] * m2[k][j];
                }
            }
        }
        return res;
    }
    public static void main(String[] args) {
        System.out.println(f(8));
        System.out.println(dp(8));
        System.out.println(quickM(8));
    }
}

  • 一个人可以一次往上迈1个台阶,也可以迈2个台阶返回这个人迈上N级台阶的方法数。
    一阶台阶有一种方法,迈一步,二阶台阶有两种方法两次一步,一次两步..=> f(1) = 1,f(2) = 2... =>n阶台阶f(n) = f(n-1)+f(n-2),就是斐波拉切数列变形,只是第二项的值不一样是二。

  • 第一年农场有1只成熟的母牛A,往后的每年:
    1)每一只成熟的母牛都会生一只母牛
    2)每一只新出生的母牛都在出生的第三年成熟
    3)每一只母牛永远不会死
    返回N年后牛的数量
    递推式:f(0) = 1,f(1) = 2,f(2) = 3,f(3) = 4,=> f(n) = f(n-1)+ f(n-3),是一个三阶的问题矩阵是一个三阶矩阵。

  • 给定一个数N,想象只由0和1两种字符,组成的所有长度为N的字符串如果某个字符串,任何0字符的左边都有1紧挨着,认为这个字符串达标
    返回有多少达标的字符串。
    定义一个函数f(i),表示在i的前面一定是1返回此时i个数达标的情况,
    情况一如果i位置是0那么i+1职位只可能是1,所以让i+2位置返回多少种方法f(i-2),情况二如果i位置是1那么让i+1位置去决策方法数f(i+1)
    所以地推式:f(n) = f(n+1)+f(n+2),又是一个斐波拉切数列问题。

蓄水池算法

解决的问题:
假设有一个源源吐出不同球的机器,只有装下10个球的袋子,每一个吐出的球,要么放入袋子,要么永远扔掉如何做到机器吐出每一个球之后,所有吐出的球都等概率被放进袋子里。
流程:先10个球直接进入袋子里面,然后当后面的球继续进入袋子的时候,让这个球以球的编号10/k(f函数,f(k)1-k等概率返回一个数,返回1-10进袋子)的概率进入袋子中,并且让袋子中的一个且等概率随机的被抛出袋子。这样所有球进入袋子的概率都相等。

/**
 * 假设有一个源源吐出不同球的机器,
 * 只有装下10个球的袋子,每一个吐出的球,要么放入袋子,要么永远扔掉
 * 如何做到机器吐出每一个球之后,所有吐出的球都等概率被放进袋子里
 */
public class Reservoir {
    // 袋子
    private static int[] bag;
    private static int count;
    private static int n ;
    public Reservoir(int n){
        // 用户需要的n大小的袋子所有进入袋子的概率都是 n/num,如果机器吐出球的数量num,
        this.bag = new int[n];
        this.count = 0;
        this.n = n;
    }
    public static void add(int a){
        if(count < n){
            bag[count] = a;
        }else if(rand(count+1) <= n){
            // 随机淘汰一个
            bag[rand(n)-1] = a;
        }
        count ++;
    }
    // 一个随机函数输入i返回1 ~ i的数字
    private static int rand(int i){
        return (int)(Math.random() * i) + 1;
    }
}

随机函数

  • 给定一个随机源函数f可以返回17,设计一个结构可以返回110。
    利用给定随机源生成一个 等概率返回0,1利用二进制返回需要范围数字。设计g函数,比如17,调用f返回123,那么代表0,返回456,那么代表1,返回7无效重新随机,110需要四位二进制,调用四次g函数,得到四位二进制,值的范围在015,如果生的数不是110中的数,那么重新调用g生成,否则返回值,那么就可以等概率返回1~10了。
/**
 * 利用1到7随机函数,设计1~10的随机函数
 */
public class OneToTenRandom {
    public static int random(){
        int g;
        while (true){
            g = g();
            if(g == 0 || g >=11){
                continue;
            }
            break;
        }
        return g;
    }

    private static int g(){
        int res = 0;
        int count = 0;
        while (count < 4){
            int temp = oneToSevenRandom();
            if(temp>=1 && temp <=3 ){
                res |= 0;
            }else if(temp >= 4 && temp <= 6) {
                res |= 1;
            }else{
                continue;
            }
            count++;
            if(count < 4){
                res <<= 1;
            }

        }
        return res;
    }
    //1~7随机生成函数
    private static int oneToSevenRandom(){
        return (int)(Math.random() * 7) + 1;
    }
    public static void main(String[] args) {
        int[] arr = new int[11];
        for(int i = 0;i < 1000000;i++){
            arr[random()]++;
        }
        int count = 0;
        for(int t : arr){
            System.out.println("count:"+count+"=>" +t);
            count++;
        }
    }
}
  • 已知函数f返回0的概率是p,返回1的概率是1-p,设计一个随机函数等概率返回1~10。
    调用2次函数f,则返回的可能,可能的结果是 01 ,11 ,10 ,00
    01 的概率 p(1-p),11的概率(1-p)^2,10的概率p(1-p),00的概率p^2,
    由于01和10的概率是相等的,我们利用这两可以等概率获取1和0,的到01代表1,的到10代表0,那么我们获取到四位二进制就可以代表一个0~15的数了,每次只返回满足条件的数就可以了。
public class P {
    // 等概率返回1 ~ 10
    public static int random2(){
        int count = 0;
        int res = 0;
        while (true){
            while (count < 4){
                int g = g();
                res |= g;
                if(count < 3){
                    res <<= 1;
                }
                count ++;
            }
            if(res == 0 || res >=11){
                res = 0;
                count = 0;
                continue;
            }
            break;
        }
       return res;
    }



    // g函数等概率返回1 和 0
    private static int g(){
        int f;
        int f2;
        while (true){
            f = f();
            f2 = f();
            if((f == 0 && f2 ==0)
                    || (f == 1 && f2 == 1)){
                continue;
            }
            break;
        }
        return (f == 0 && f2 == 1) ? 1 : 0;
    }

    // 返回0的概率是p,返回1的概率是1-p,
    // 假设p是0.3
    private static int f(){
        // 调用h函数如果返回1~3那么返回0,如果返回4~10 返回1
        int res;
        if(h()>=1 && h()<=3){
            res = 0;
        }else{
            res = 1;
        }
        return res;
    }

    // 等概率返回1 ~ 10
    private static int h(){
        return (int)(Math.random() * 10)+1;
    }
    public static void main(String[] args) {
        int[] arr = new int[11];
        for(int i = 0;i < 100000;i++){
            arr[random2()]++;
        }
        int count = 0;
        for(int t : arr){
            System.out.println("count:"+count+"=>" +t);
            count++;
        }
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342