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,求方案数。

    待续。。 

  • 相关阅读:
    第一个VS2015 Xaramin Android项目(续)
    第一个VS2015 Xaramin Android项目
    Android SDK Manager 无法打开
    VS 2015 Android 环境设置
    Unity 游戏运行越久加载越慢
    一不注意,在Unity3D中DllImport 引起的Bug.
    Animation显示ListView的每一条记录
    ViewFlipper
    BitmapFactory.Options对图片进行缩放
    显示倒计时的Button按钮
  • 原文地址:https://www.cnblogs.com/wenruo/p/7414964.html
Copyright © 2011-2022 走看看