在没有Numpy的情况下对嵌套列表进行递归迭代

时间:2018-05-27 13:48:51

标签: python numpy slice matrix-multiplication

我想迭代一个嵌套列表。我只找到了Numpy的解决方案(在其他情况下是>),但我想知道如果没有Numpy这是否可行。

import numpy as np

def matrix_mulitplication(A, B):
    n = A.shape[0]

    if n == 1:
        return A * B
    else:
        i = int(n / 2)

        C = np.zeros((n, n), dtype=np.int)

        C[:i, :i] = matrix_mulitplication(A[:i, :i], B[:i, :i]) + matrix_mulitplication(A[:i, i:], B[i:, :i])

        C[:i, i:] = matrix_mulitplication(A[:i, :i], B[:i, i:]) + matrix_mulitplication(A[:i, i:], B[i:, i:])

        C[i:, :i] = matrix_mulitplication(A[i:, :i], B[:i, :i]) + matrix_mulitplication(A[i:, i:], B[i:, :i])

        C[i:, i:] = matrix_mulitplication(A[i:, :i], B[:i, i:]) + matrix_mulitplication(A[i:, i:], B[i:, i:])

        return C

x = np.array([[1, 2], [3, 4]])
y = np.array([[1, 2], [3, 4]])

print(matrix_mulitplication(x, y))

输出:

[[ 7 10]
 [15 22]]

我对第一个切片案例的想法:

c_one = [C[i][0:int(len(C) / 2)] for i in range(0,int(len(C) / 2))]

2 个答案:

答案 0 :(得分:0)

numpy中,您只是进行二维矩阵乘法,可以用多种方式表示,例如np.dotnp.einsum或(新的)@运算符

In [1]: x = np.array([[1, 2], [3, 4]])
In [2]: x@x
Out[2]: 
array([[ 7, 10],
       [15, 22]])

要对列表执行相同操作,请让我们按步骤处理任务

在代数中,我学会了用一根手指划过各行,另一根指向列。因此,让我们使用列表理解:

In [6]: x1 = x.tolist()
In [7]: x2 = x.tolist()
In [8]: [[(row1,col2) for col2 in zip(*x2)] for row1 in x1]
Out[8]: [[([1, 2], (1, 3)), ([1, 2], (2, 4))], [([3, 4], (1, 3)), ([3, 4], (2, 4))]]

行和列的配对看起来正确。 zip(*xl)是一种用于转置'的习惯用语。一个列表(相当于numpy中的x.T

现在定义一个执行1d乘法的辅助函数:

In [9]: def mult(row,col):
   ...:     return [i*j for i,j in zip(row,col)]
   ...: 
   ...: 
In [10]: [[mult(row1,col2) for col2 in zip(*x2)] for row1 in x1]
Out[10]: [[[1, 6], [2, 8]], [[3, 12], [6, 16]]]

现在添加总和。我可以回去修改mult,但在外部理解中这样做也很容易。

In [12]: [[sum(mult(row1,col2)) for col2 in zip(*x2)] for row1 in x1]
Out[12]: [[7, 10], [15, 22]]

或者将所有列表推导结合起来,制作一个不可读的行:

In [14]: [[sum(i*j for i,j in zip(row1,col2)) for col2 in zip(*x2)] for row1 in x1]
Out[14]: [[7, 10], [15, 22]]

对于这个2x2示例,我的结果与您的结果相同,但是您采用了不同的方法,将原始数组拆分为块。可以使用切片很好的numpy数组正常工作。但我不确定它对列表有帮助。我们必须与其他例子一起玩。

Mine使用3x3阵列

In [29]: y = np.arange(9).reshape(3,3)
In [30]: [[sum(mult(row1,col2)) for col2 in zip(*y.tolist())] for row1 in y.toli
    ...: st()]
Out[30]: [[15, 18, 21], [42, 54, 66], [69, 90, 111]]
In [31]: y@y
Out[31]: 
array([[ 15,  18,  21],
       [ 42,  54,  66],
       [ 69,  90, 111]])

您的3x3上的形状广播错误失败,但在4x4上的效果相同。

In [32]: y = np.arange(16).reshape(4,4)
In [33]: y@y
Out[33]: 
array([[ 56,  62,  68,  74],
       [152, 174, 196, 218],
       [248, 286, 324, 362],
       [344, 398, 452, 506]])
In [34]: [[sum(mult(row1,col2)) for col2 in zip(*y.tolist())] for row1 in y.toli
    ...: st()]
Out[34]: 
[[56, 62, 68, 74],
 [152, 174, 196, 218],
 [248, 286, 324, 362],
 [344, 398, 452, 506]]
In [35]: matrix_mulitplication(y,y)
Out[35]: 
array([[ 56,  62,  68,  74],
       [152, 174, 196, 218],
       [248, 286, 324, 362],
       [344, 398, 452, 506]])

因此,您的递归是将4x4分解为2x2块然后降至1x1。我将2d阵列分成1d产品总和。

答案 1 :(得分:0)

尝试复制你的nxn块细分。我使用blk辅助函数来复制2d数组切片:

import numpy as np

def matrix_mulitplication(A, B):
    def blk(x,i1,i2):
        return [row[i2] for row in x[i1]]

    n = len(A) # A.shape[0]

    if n == 1:
        #print(A)
        return A[0][0] * B[0][0]
    else:
        i = int(n / 2)
        i1, i2 = slice(None,i), slice(i,None)
        #C = np.zeros((n, n), dtype=np.int)
        C1 = matrix_mulitplication(blk(A, i1, i1), blk(B,i1,i1)) +\
             matrix_mulitplication(blk(A, i1, i2), blk(B,i2,i1))

        C2 = matrix_mulitplication(blk(A, i1, i1), blk(B,i1,i2)) +\
             matrix_mulitplication(blk(A, i1, i2), blk(B,i2,i2))

        C3 = matrix_mulitplication(blk(A, i2, i1), blk(B,i1,i1)) +\
             matrix_mulitplication(blk(A, i2, i2), blk(B,i2,i1))

        C4 = matrix_mulitplication(blk(A, i2, i1), blk(B,i1,i2)) +\
             matrix_mulitplication(blk(A, i2, i2), blk(B,i2,i2))
        C = [[C1,C2],[C3,C4]]
        return C

x = np.array([[1, 2], [3, 4]])
y = np.arange(16).reshape(4,4)

z = matrix_mulitplication([[1]],[[2]])
print(z)
z = matrix_mulitplication(x.tolist(), x.tolist())
print(z)
print(x@x)
z = matrix_mulitplication(y.tolist(), y.tolist())
print(z)
print(y@y)

这适用于一级递归,但不适用于两种:

1253:~/mypy$ python3 stack50552791.py 
2
[[7, 10], [15, 22]]
[[ 7 10]
 [15 22]]
[[[[4, 5], [20, 29], [52, 57], [132, 145]], [[6, 7], [38, 47], [62, 67], [158, 171]]], [[[36, 53], [52, 77], [212, 233], [292, 321]], [[70, 87], [102, 127], [254, 275], [350, 379]]]]
[[ 56  62  68  74]
 [152 174 196 218]
 [248 286 324 362]
 [344 398 452 506]]

第二级的问题是matrix_mulitplication返回一个嵌套列表,列表+被定义为连接,而不是元素添加。因此,我必须定义另一个辅助函数(或2)来正确解决这个问题。

好一点

def matrix_mulitplication(A, B):
    def blk(x,i1,i2):
        return [row[i2] for row in x[i1]]
    def add(x,y):
        if isinstance(x, list):
            return [add(i,j) for i,j in zip(x,y)]
        else:
            return x+y
    n = len(A) # A.shape[0]

    if n == 1:
        return A[0][0] * B[0][0]
    else:
        i = int(n / 2)
        i1, i2 = slice(None,i), slice(i,None)
        #C = np.zeros((n, n), dtype=np.int)
        C1 = add(matrix_mulitplication(blk(A, i1, i1), blk(B,i1,i1)) ,\
             matrix_mulitplication(blk(A, i1, i2), blk(B,i2,i1)))

        C2 = add(matrix_mulitplication(blk(A, i1, i1), blk(B,i1,i2)) ,\
             matrix_mulitplication(blk(A, i1, i2), blk(B,i2,i2)))

        C3 = add(matrix_mulitplication(blk(A, i2, i1), blk(B,i1,i1)) ,\
             matrix_mulitplication(blk(A, i2, i2), blk(B,i2,i1)))

        C4 = add(matrix_mulitplication(blk(A, i2, i1), blk(B,i1,i2)) ,\
             matrix_mulitplication(blk(A, i2, i2), blk(B,i2,i2)))
        C = [[C1,C2],[C3,C4]]
        return C

和4x4案例

[[[[56, 62], [152, 174]], [[68, 74], [196, 218]]], [[[248, 286], [344, 398]], [[324, 362], [452, 506]]]]
[[ 56  62  68  74]
 [152 174 196 218]
 [248 286 324 362]
 [344 398 452 506]]

数字全部存在,但是在4深度嵌套列表中。

因此,不仅将嵌套列表拆分为2d块比使用数组更尴尬,重新组装它们也很尴尬。

https://en.wikipedia.org/wiki/Strassen_algorithm

在没有详细阅读Strassen算法的情况下,很明显任何关于其效率的声明都假定A_ij索引(对于单个元素或块)在获取值和设置时都是有效的({{1} })。对于列表,C_ijA[i]效率很高,但A[i:j]不是。

使用[row[i2] for row in x[i1]组装块是可以的,但是与[[a,b],[c,d]]相当的任何内容[[C_11,C_12],[C_21,C_22]]元素都是块而不是标量,这很复杂。

似乎Strassen的目标是减少所需的乘法次数。这假设(标量)乘法是矩阵乘法中最昂贵的部分。使用Python列表显然不是这样的。访问元素更加昂贵。

a numpy cheat

我可以使用

从4x4案例中修改C
z

这在arr = np.array(z) arr = arr.transpose(0,2,1,3).reshape(4,4) 中更容易。