1. 程式人生 > >數的快速冪與矩陣的快速冪

數的快速冪與矩陣的快速冪

快速冪

對於任意底數的冪結果都可以分解為指數為2的冪的結果相乘。

因為任意一個數可以用二進位制來表示,所以我們的指數(假設為b),可以寫成

b=b020+b121+b222+......+bn2n(1(其中,各子項的係數為0或1,因為二進位制)

所以我們的

ab=ab020ab121ab222ab020......abn2n2
 比如3^6 = 3^4*3^2

所以我們可以利用以上的結論,思想分解我們的指數為2的冪的多項式的形式,然後依次從低位考察這個多項式,若二進位制某一位為0,則該子項為0,那麼以該子項為冪的結果就為1,所以我們跳過該位。我們還發現式子2的每一個項都是前一項的平方,所以我們在考察指數多項式的每一位時,不管該位是不是0,都應該記錄該子項的結果,方便遇到二進位制為1時直接累乘該值即可,減少相乘次數。具體演算法如下:

/**
     * 快速冪
     * @param num 底數
     * @param n   指數
     * @return
     */
    public static int fastPower(int num, int n){
        int result = 1;
        int digit = num;
        while(n != 0){
            if((n & 1) == 1){  //該數位為1, 意思分解為冪相乘時,有該數
                result *= digit; 
            }
            n >>= 1
; digit *= digit; //指數每右移一位,指數相當於乘以2,對應到冪結果應執行一次平方 } return result; }

複雜度分析

普通求冪需要連乘n次,所以複雜度為O(n)。而採用快速冪,把數的相乘按照指數二進位制形式的合併了,且合併規模的為上一次的二倍。比如求2的9次方,正常需要連乘9次,而採用快速冪可以降為6次。9的二進位制為4位,故需要4次乘法記錄當前中間值(該中間值主要為了減少乘法次數,方便遇到二進位制為1使用該值),外加2次的二進位制為1時需要乘以中間值。因為問題規模為自底向上倍增上去,故複雜度為O(logn)。

矩陣的快速冪

因為矩陣相乘滿足結合率,所以,我們可以擴充套件到二維陣列上,也就是求矩陣的冪。而且矩陣的快速冪原理與數的快速冪是一致的,都是利用指數的二進位制表現形式,不同的是我們需額外實現矩陣相乘演算法。

    /**
     * 矩陣快速冪
     * @param base
     * @param index
     * @return
     */
    public static int[][] fastPowMatrix(int[][] base, int index) {
        int[][] res = new int[base.length][base[0].length];
        for(int i = 0; i < base.length; i++) {
            res[i][i] = 1;
        }
        int[][] t = base;
        while(index != 0) {
            if((index & 1) == 1) {
                res = multipleMatix(res, t);
            }
            t = multipleMatix(t, t);
            index >>= 1;
        }
        return res;
    }

    /**
     * 矩陣相乘
     * @param left
     * @param right
     * @return
     */
    public static int[][] multipleMatix(int[][] left, int[][] right) {
        int[][] res = new int[left.length][left[0].length];
        for(int i = 0; i < left.length; i++) {
            for(int j = 0; j < right.length; j++) {
                for(int k = 0; k < left[0].length; k++) {
                    res[i][j] += left[i][k] * right[k][j];
                }
            }
        }
        return res;
    }