zoukankan      html  css  js  c++  java
  • 快速幂小结

    (本文不涉及取模运算……)

    快速幂,顾名思义,就是快速地求幂运算。

    现在要求x=yn的值,最朴素的解法:

    int pow(int y, int n) {
        int result = 1;
        for (int i = 0; i < n; i++) {
            result *= y;
        }
        return result;
    }

    x=pow(y,n)

    复杂度是O(n)

    当n是偶数的时候,我们设n=2*m,则x=yn=y2*m=(ym)^2

    当n是奇数的时候,我们设n=2*m+1,则x=yn=y2*m+1=y*(ym)^2

    这样,我们就把复杂度从O(n)降到了O(n/2),递归下去,算法的复杂度就是O(log2n)……该怎么解释这个……其实我不会……

    用递归实现比较容易理解,具体代码:

    int quick_pow(int y, int n) {
        if (n == 0) return 1;
        if (n % 2 == 1) {
            // n是奇数 n=2*m+1
            // x = y^(2*m+1) = y * (y^m)^2
            int m = n / 2;
            int temp = quick_pow(y, m);
            return y * temp *temp;
        } else {
            // n是偶数 n=2*m
            // x = y^(2*m) = (y^m)^2
            int m = n / 2;
            int temp = quick_pow(y, m);
            return temp *temp;
        }
    }

    可以简化一下~~

    int quick_pow(int y, int n) {
        if (!n) return 1;
        int temp = quick_pow(y, n >> 1);
        if (n & 1) return y * temp *temp;
        else return temp *temp;
    }

    非递归的版本也很容易理解啦

    int quick_pow(int y, int n) {
        int result = 1;
        while (n > 0) {
            if (n % 2 == 1) {
                // y^n = y^(2*m+1) = y*(y^2)^m
                result *= y;
                // 接下来让y=y^2, n=m
                y = y * y;
                n = n / 2;
            } else {
                // y^n = y^(2*m) = (y^2)^m
                y = y * y;
                n = n / 2;
            }
        }
        return result;
    }

    简化一下~~~

    int quick_pow(int y, int n) {
        int result = 1;
        while (n) {
            if (n & 1) result *= y;
            y = y * y;
            n >>= 1;
        }
        return result;
    }

    矩阵快速幂

    我们都知道斐波那契数列(Fibonacci sequence),1、1、2、3、5、8、13、21、34……每一项是前两项的和。

    如果要求第n项,需要从第一项开始递推。

    int fibonacci(int n) {
        int a[n];
        a[0] = 1, a[1] = 1;
        for (int i = 2; i < n; i++) {
            a[i] = a[i-1] + a[i-2];
        }
        return a[n-1];
    }

    复杂度很容易看出来,O(n)。

    我们都是学过线性代数的人,那么也就知道矩阵。这里斐波那契数列可以通过矩阵来算。

    首先复习一下矩阵乘法的公式

    就是当我们求第 i 行第 j 列时,就使用第一个矩阵的第 i 行每一个元素和第二个矩阵第 j 列每一个元素分别相乘然后求和。所以第一个矩阵的列数要等于第二个矩阵的行数。

    随便画一幅图理解下,

    矩阵是怎么作用到这里的呢,我们设斐波那契数列为一个数组a[],那么a[0]=1,a[1]=1,a[n]=a[n-1]+a[n-2]

    通过这个递推公式,我们可以得出

    例如,

    我们知道矩阵乘法满足结合律,接下来我们可以得出

    设构造的矩阵为F

    于是我们求a[n]的重点变成了求矩阵F的n次幂,此处可以直接将上文所述快速幂应用到这里。

    注意构造的矩阵F必须是方阵,也就是行列必须相等,矩阵乘法的复杂度是O(k^3),k是矩阵的行数。所以总复杂度就是O(log2n*k^3),因为一般k都非常小,所以总体复杂度大大降低。

    矩阵乘法的代码比较简单,不想写了。。。。(才不说是忘了怎么写了。。

    这样我们可以用矩阵快速求出所有类似的递推数列的第n项。

    比如:a[n] =a[n-1]+a[n-3]+a[n-4]

    还是斐波那契数列,如果我们想求前n项的和,a[1]+a[2]+……+a[n],依然可以通过矩阵快速幂求得、需求要简单的加一行用来记录当前和,

    这样,

    是不是好厉害,好神奇!

    例题,如果你一次可以走1~k个楼梯,那么你要走到第n个楼梯有多少种走法?(k<n)

    dp[n] = dp[n-1]+dp[n-2]+...+dp[n-k]

    然后写出转移矩阵,快速幂求解即可。

    例题 HDU5863

    有k种不同的字符,每种字符有无限个,要求用这k种字符构造两个长度为n的字符串a和b,使得a串和b串的一一对应的连续相同字符长度不大于m,求方案数。

    待续。。 

  • 相关阅读:
    easy ui 表单ajax和from两种提交数据方法
    easy ui 下拉级联效果 ,下拉框绑定数据select控件
    easy ui 下拉框绑定数据select控件
    easy ui 异步上传文件,跨域
    easy ui 菜单和按钮(Menu and Button)
    HTTP 错误 404.3
    EXTJS4.2 后台管理菜单栏
    HTML 背景图片自适应
    easy ui 表单元素input控件后面加说明(红色)
    EXTJS 4.2 添加滚动条
  • 原文地址:https://www.cnblogs.com/wenruo/p/7414964.html
Copyright © 2011-2022 走看看