Python在大整数矩阵乘积上的精度和性能

时间:2018-05-18 14:54:46

标签: python numpy precision sympy matrix-multiplication

我想计算两个量A和B的乘积,然后计算乘积F的乘积。通常,A和B是具有大数的矩阵,因此操作是传统的矩阵乘法。让我们考虑简单A和B作为标量。我测试的所有方法的Python代码如下:

from __future__ import division
import numpy as np
from sympy import Matrix
from sympy import *

A = 2251875000001
B = 28839630

F = 33232924804801

#Method 1: pure Python
C1 = (A*B) % F

A_mat = np.matrix(A).astype(np.int64)
B_mat = np.matrix(B).astype(np.int64)

#Method 2: numpy matrices and pure Python
C2 = (A_mat*B_mat) % F

#Method 3: numpy
C3 = np.dot(A, B) % F

#Method 4: numpy
C4 = np.dot(A_mat, B_mat) % F

#Method 5: Python objects and numpy
A_mat2 = np.matrix(A, dtype=object)
B_mat2 = np.matrix(B, dtype=object)
C5 = np.dot(A_mat2, B_mat2) % F
C5 = np.concatenate(C5).astype(np.int64)

#Method 6: sympy
A_sp = Matrix(1,1,[A])
B_sp = Matrix(1,1,[B])
C6 = A_sp*B_sp
f = lambda x: x%F
C6 = C6.applyfunc(f)
print(C6)
C6 = matrix2numpy(C6, dtype='int64')

现在结果应该是

(2251875000001 * 28839630) mod 33232924804801 = 25112458407047

当我运行上面的代码时,我得到的C1 == 25112458407047对于这个例子是正确的,但是当我测试大矩阵的乘法时,我用这种方法获得的大多数条目都是错误的。但是,C2C3C4的值均等于12062945446480,这是错误的。 C5C6也会正确计算。

您可以假设int64的64位精度对于我正在使用的数字来说已经足够了,但默认的32位不是。

我尝试过Sympy(参见方法6),因为根据here它应该具有任意精度。

我使用的是Python 2.7.14,Numpy 1.14.2和Sympy 1.1.1。

我的第一个问题是为什么我在上面的代码中得到了一些错误的结果?

最后,即使方法5和6总是正确的,但它们似乎比其他方法慢。你能解释一下吗?上述方法在矩阵乘法的复杂性方面有何不同,您的建议是什么?

修改

我认为这应该从我在代码中使用的函数中清楚地看出,但无论如何,我感兴趣的操作是传统的矩阵乘法。另外,我为我的例子实际上是溢出这一事实道歉,但性能问题仍然有效。

1 个答案:

答案 0 :(得分:0)

如注释中所述,A * B不适合64位,因此它被截断,使得基于int64的计算返回错误的结果。因为int64不足以容纳A*B,所以下一个最好的事情是使用数据类型为“object”的数组(基本上是你的方法5,尽管是时候让np.matrix抛弃了):

A_obj = np.array(A).astype(object)
B_obj = np.array(B).astype(object)
np.dot(A_obj, B_obj) % F

返回25112458407047

它比int64乘法慢,但仍然比SymPy快(在我的测试中大约是15倍)。

np.matrix的存在是模仿Matlab语法的黑暗时代的残余;在新代码中不建议使用它。在我的测试中使用np.matrix的速度大约是其3倍。

  

方法5和6似乎比其他方法慢。

处理更复杂的对象需要更长时间。使用能够乘以整数的低级例程处理int64数字数组是一回事;另一个是处理一个对象数组,这些对象具有在它们上面实现的任何类型的乘法运算符。前者可以(并且是)在C或Fortran级别完成,将结果返回给Python;但是要处理Python对象,必须始终使用Python。