快速方阵幂算法

一、问题描述

    给定一个方阵 a,求方阵 a 的 n 次幂  a^n

二、简单算法

    直接想法是利用for循环不断进行方阵乘法,直到求出 a^n,算法时间复杂度是 m^3n,如此高的时间复杂度在实践中是走不通的。

三、快速算法

    下面是用python实现的 2\times 2 方阵的快速实现。n 右移一位最多执行 \log n 次就会变成 0,故算法的时间复杂度是 m^3\log nm 表示方阵的行数或列数,基本思想是复用已经计算出来的部分结果,减少重复计算。

# !/usr/bin/python
# coding=utf-8

# 两个方阵的乘法
def multiply(a, b):
    c = [[1,0],[0,1]]
    for i in range(0,2):
        for j in range(0,2):
            c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j]
    return c

# 迭代法
def quick_matrix_pow(a,n):
    ans = [[1,0],[0,1]]
    while n > 0:
        # 如果n的二进制表示最低位是1
        if n & 1 == 1:
            ans = multiply(ans, a)
        n >>= 1
        if n > 0:
            a = multiply(a, a)
    return ans

'''
# 递归法,n太大可能造栈溢出
def quick_matrix_pow(a,n):
    if n == 1:
        return a
    c = f(a,n/2)
    d = multiply(c,c)
    if n % 2 == 0:
        return d
    else:
        return multiply(d,a)
'''

def main():
    a = [[1,3],[2,2]]
    n = 9
    ans = quick_matrix_pow(a, n)
    for i in range(0,2):
        print("[%d\t%d]" % (ans[i][0],ans[i][1]))

if __name__ == '__main__':
    main()


# 输出
[104857 157287]
[104858 157286]

四、快速算法的正确性

    快速算法的代码实现非常简洁,但是如何理解它的正确性那?通过下面的推理可以直观的来解释算法的正确性,n 分别等于8、9、11。

                     \large 00001000\rightarrow ans=A^8

                     \large 00001001\rightarrow ans=A\rightarrow ans=A\ast A^8

                     \large 00001011\rightarrow ans=A\rightarrow ans=A\ast A^2\rightarrow ans=A\ast A^2\ast A^8

    由算法描述,每次循环先检查 n 的二进制表示的最低位是否为1,是1的话就对ans赋值为 \large A^{i}, \large i 表示该最低位在 n 的二进制表示中所处的位置;否则, 将 n 的二进制表示右移一位(高位会进行补0),令 A=A^2

    一般的,如果  n 的二进制表示有 \large m 位,则有

                                  \large A^n=\prod_{i=1}^{m}A^{i\cdot b_i}

    其中,\large b_i 是 n 的二进制表示中第 \large i 位的值(0或者1),简单来说,就说忽略为 0 的位,值为1的位计算 \large A^i,再进行累乘。

五、在斐波那契数列上的应用

    斐波那契递推公式为:f(n)=f(n-1)+f(n-2),如果 n 不是很大的话,直接根据公式迭代便可求出 f(n),代码如下。递归解法(结合查表)和迭代解法复杂度相同,但是多了很多栈空间的操作,而且容易造成栈溢出,故不推荐采用递归解。

# !/usr/bin/python
# coding=utf-8

def fibonacci(n):
    if n < 0:
        raise Exception("Must be positive integer")
    if n <= 1:
        return n
    a = 0
    b = 1
    c = a + b
    # 采用了滚动数组
    while n > 2:
        a = b
        b = c
        c = a + b
        n = n - 1
    return c

def main():
    n = 2
    print(fibonacci(n))

if __name__ == '__main__':
    main()

    在我的机器上,当 n = 1000000时,运算时间已经达到5秒左右,如果 n = 100000000,速度是超级慢的,该算法的复杂度是 O(n)

    但是,仔细观察可以发现

                     \begin{bmatrix} f(n)\\ f(n-1) \end{bmatrix}=\begin{bmatrix} 1 &1 \\ 1 &0 \end{bmatrix}\cdot \begin{bmatrix} f(n-1)\\ f(n-2) \end{bmatrix}

    递推的展开上式有

                     \begin{bmatrix} f(n)\\ f(n-1) \end{bmatrix}=\begin{bmatrix} 1 &1 \\ 1 &0 \end{bmatrix}^{n-1}\cdot \begin{bmatrix} f(1)\\ f(0) \end{bmatrix}

     现在关键问题就是求

                     \begin{bmatrix} 1 &1 \\ 1 &0 \end{bmatrix}^{n-1}

     这是快速方阵幂算法所解决的问题, 因此有了这个中间过程的解, f(n) 自然可以求出,下面是使用了快速方阵幂的斐波那契算法。

# !/usr/bin/python
# coding=utf-8

from quick_matrix_pow import *
import time

def QuickFibonacci(n):
    if n < 0:
        raise Exception("Must be positive integer")
    if n <= 1:
        return n
    m = [[1,1],[1,0]]
    a = quick_matrix_pow(m, n-1)
    return a[0][0]

def main():
    n = 10000000
    QuickFibonacci(n)

if __name__ == '__main__':
    main()

当 n = 10000000,时间大约用了21秒,而普通的迭代算法已经超过了1分钟,算法的时间复杂度同快速矩阵幂算法。 

六、思想推广

    快速方阵幂的思想可以用来解决很多具有迭代性质的问题,斐波那契是其中的特例,遇到具有迭代性质的问题时,不妨考虑下可否借鉴快速方阵幂的实现来加快迭代算法的求解。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值