2025年矩阵快速幂

矩阵快速幂转 http huanyouchen github io 2018 05 23 Quick Matrix Pow 矩阵的快速幂是用来高效地计算矩阵的高次方的 将朴素的 o n 的时间复杂度 降到 log n 本文先学习快速幂和矩阵乘法的基础知识 然后将两者结合实现矩阵快速幂方法

大家好,我是讯享网,很高兴认识大家。

转:http://huanyouchen.github.io/2018/05/23/Quick-Matrix-Pow/


快速幂

一般计算底数x的n次幂xnxn 的方法: xn=x×x×x…x×xxn=x×x×x…x×x ,需要做n次乘法运算,代码实现如下:

 

讯享网

1

2

3

4

5

6

7

讯享网 

def powx(x, n):

res = 1

for i in range(n):

res = res * x

return res

print(powx(x,n))

时间复杂度为O(N), 当n非常大时候运算效率很低。怎么才能提高运算效率来快速计算底数x的n次幂呢?可以通过快速幂方法。

先用一个具体的例子解释其原理:比如,计算x的11次方x11x11,而11可以用二进制表示:11=1011=1×23+0×22+1×21+1×2011=1011=1×23+0×22+1×21+1×20

原先需要做11次乘法运算,转换后只需要3次乘法运算。

通过上面的具体例子来推导一般情况计算xnxn 的方法,先把n转换为2进制,从低位到高位根据二进制中的0或者1来进行乘法运算,比如上面的x11x11例子,从低位到高位的运算过程:

  • 11 = 1011
  • 1:res = x20x20
  • 1: res = x20×x21x20×x21
  • 0: 跳过,不运算
  • 1:res = x20×x21×x23x20×x21×x23

得到结果res = x1×x2×x8=x11x1×x2×x8=x11

判断n的二进制低位是0或者1的方法, 也就是判断n是偶数或者奇数的方法,可以通过位运算and来实现:

  • n and 1 返回1, 则n是奇数,即n的二进制低位是1
  • n and 1 返回0, 则n是偶数,即n的二进制低位是0

从低位到高位的运算过程也可以通过位运算>>实现, n = n >> 1, 把n的二进制位右移过程也就是高位到低位的过程。比如11 = 1011的右移过程:

  • n = n >> 1 = 1011 >> 1 = 101
  • n = n >> 1 = 101 >> 1 = 10
  • n = n >> 1 = 10 >> 1 = 1
  • n = n >> 1 = 1 >> 1 = 0

根据上面分析,快速幂的代码实现:

 

1

2

3

4

5

6

7

8

9

10

讯享网 

def powx(x, n):

res = 1

while n:

if n&1:

res = res * x

x = x * x

n = n >> 1

return res

print(powx(x,n))

上面快速幂代码中第5,6行涉及到两步乘法运算,当x很大时,比如, 这样直接乘效率也比较低,可以通过快速乘法进一步优化:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

讯享网 

#快速乘法

def fast_multi(a,b):

multi_res = 0

while b:

if b&1:

multi_res = multi_res + a

a = a+a

b >>= 1

return multi_res

#快速幂

def powx(x, n):

res = 1

while n:

if n&1:

res = fast_multi(res, x)

x = fast_multi(x, x)

n = n >> 1

return res


矩阵乘法

矩阵:一个m×nm×n的矩阵就是m×nm×n个数排成m行n列的一个数阵。

矩阵乘法举例:

令A=[1234]A=[1234] , B=[5678]B=[5678], 则:

C=AB=[1234]×[5678]=[1×5+2×71×6+2×83×5+4×73×6+4×8]=[]C=AB=[1234]×[5678]=[1×5+2×71×6+2×83×5+4×73×6+4×8]=[]

代码实现:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

讯享网 

def MatrixMultiply(matrix_a, matrix_b):

n_row = len(matrix_a)

n_col = len(matrix_b[0])

# C的行数等于A的行数,C的列数等于B的列数

matrix_c = [[0 for j in range(n_col)] for i in range(n_row)]

for i in range(0, n_row):

for j in range(0, n_col):

for k in range(0, n_row):

matrix_c[i][j] = matrix_c[i][j] + matrix_a[i][k] * matrix_b[k][j]

return matrix_c

a = [[1,2],[3,4]]

b = [[5,6],[7,8]]

c = MatrixMultiply(a, b)

print(c)

# [[19, 22], [43, 50]]

且矩阵乘法满足结合律:

A11=A8×A2×A1=[1234]8×[1234]2×[1234]1A11=A8×A2×A1=[1234]8×[1234]2×[1234]1


矩阵快速幂

现在知道了两个矩阵乘法运算,那么如果求矩阵A的n次方AnAn,可以用An=A×A×A×…×AAn=A×A×A×…×A,不过学习了计算底数xx的n次幂xnxn 的快速幂方法,把底数xx换成矩阵A,计算AnAn,使用上面快速幂的方法同样可以实现矩阵快速幂。

结合快速幂和矩阵乘法,实现代码:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17


讯享网

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

讯享网 

def MatrixMultiply(matrix_a, matrix_b):

n_row = len(matrix_a)

n_col = len(matrix_b[0])

# C的行数等于A的行数,C的列数等于B的列数

matrix_c = [[0 for j in range(n_col)] for i in range(n_row)]

for i in range(0, n_row):

for j in range(0, n_col):

for k in range(0, n_row):

matrix_c[i][j] = matrix_c[i][j] + matrix_a[i][k] * matrix_b[k][j]

return matrix_c

def getUnitMatrix(l):

# 构造单位矩阵,l为矩阵a的行数

unit_matrix = [[0 for j in range(l)] for i in range(l)]

for k in range(l):

unit_matrix[k][k] = 1

return unit_matrix

def QuickMatrixPow(a, n):

# res_matrix初始化为单位矩阵

res_matrix = getUnitMatrix(len(a))

while n:

if n&1:

res_matrix = MatrixMultiply(res_matrix, a)

a = MatrixMultiply(a,a)

n = n >> 1

return res_matrix

a = [[1,2],[3,4]]

n = 11

print(QuickMatrixPow(a,n))

# [[, ], [, ]]


扩展:斐波那契数列矩阵算法

关于斐波那契数列的定义:

斐波那契数列(意大利语:Successione di Fibonacci),又译为费波拿契数列、费波那西数列、斐波那契数列、黄金分割数列。

在数学上,费波那契数列是以递归的方法来定义:

  • F(0)=0F(0)=0
  • F(1)=1F(1)=1
  • F(n)=F(n−1)+F(n−2)(n>=2)F(n)=F(n−1)+F(n−2)(n>=2)

用文字来说,就是费波那契数列由0和1开始,之后的费波那契系数就是由之前的两数相加而得出。首几个费波那契系数是:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……(OEIS中的数列A000045)

特别指出:0不是第一项,而是第零项。

维基百科:斐波那契数列

得到: [F(n)F(n−1)]=[1110]n−1×[F(1)F(0)][F(n)F(n−1)]=[1110]n−1×[F(1)F(0)]

我们知道[F(1)=1F(0)=0][F(1)=1F(0)=0],且[1110]n−1[1110]n−1可以用上面介绍的矩阵快速幂计算出来,两者相乘得到矩阵的第1行第1列就是F(n)F(n)了。

实现代码:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

讯享网 

def MatrixMultiply(matrix_a, matrix_b):

n_row = len(matrix_a)

n_col = len(matrix_b[0])

# C的行数等于A的行数,C的列数等于B的列数

matrix_c = [[0 for j in range(n_col)] for i in range(n_row)]

for i in range(0, n_row):

for j in range(0, n_col):

for k in range(0, n_row):

matrix_c[i][j] = matrix_c[i][j] + matrix_a[i][k] * matrix_b[k][j]

return matrix_c

def get_unit_matrix(l):

unit_matrix = [[0 for j in range(l)] for i in range(l)]

for k in range(l):

unit_matrix[k][k] = 1

return unit_matrix

def QuickMatrixPow(a, n):

res_matrix = get_unit_matrix(len(a))

while n:

if n&1:

res_matrix = MatrixMultiply(res_matrix, a)

a = MatrixMultiply(a,a)

n = n >> 1

return res_matrix

def get_Fib_n(n):

if n == 0:

return 0

elif n == 1:

return 1

else:

a = [[1,1],[1,0]]

base = [[1],[0]]

Fib_n = MatrixMultiply(QuickMatrixPow(a,n-1),base)

return Fib_n[0][0]

if __name__ == '__main__':

# 求斐波那契数列第F(n)个数

n = 8

print(get_Fib_n(n))

# 21

结束。

小讯
上一篇 2025-02-17 16:07
下一篇 2025-01-27 19:26

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/49873.html