Python 无Numpy嵌套列表上的递归迭代

Python 无Numpy嵌套列表上的递归迭代,python,numpy,slice,matrix-multiplication,Python,Numpy,Slice,Matrix Multiplication,我想迭代一个嵌套列表。我只找到了一个使用Numpy(->在else的情况下)的解决方案,但我想知道如果没有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)

我想迭代一个嵌套列表。我只找到了一个使用Numpy(->在else的情况下)的解决方案,但我想知道如果没有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))]

numpy
中,您只需执行二维矩阵乘法,它可以用多种方式表示,例如
np.dot
np.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。Mine将2d数组分解为一维乘积和

尝试复制nxn块细分。我使用了
blk
helper函数来复制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\u mulitapplication
返回一个嵌套列表,列表
+
被定义为串联,而不是元素添加。因此,我必须定义另一个helper函数(或2)来正确地解决这个问题

好一点 对于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块比使用数组更为棘手,而且重新组合它们也很棘手

在不详细阅读Strassen算法的情况下,很明显,任何关于其效率的声明都假定
A_ij
索引(对于单个元素或块)在获取值和设置(
C_ij
)时都是有效的。对于列表,
A[i]
A[i:j]
是相当有效的,但是x[i1]中的行的
[row[i2]不是

[[a,b],[c,d]
组装块是可以的,但是任何类似
[[c_11,c_12],[c_21,c_22]
的元素都是块而不是标量,这是很复杂的

Strassen的目标似乎是减少所需的乘法次数。这假设(标量)乘法是矩阵乘法中最昂贵的部分。对于Python列表,情况显然不是这样。访问元素的成本更高

小骗子 我可以用

arr = np.array(z)
arr = arr.transpose(0,2,1,3).reshape(4,4)

这在
numpy

两级嵌套中就容易多了?@YakymPirozhenko确切地说是
itertools.chain(*nested_list)
?@NickA首先更新了代码,谢谢你的详细解释。我知道矩阵乘法有更简单的方法,但我的方法是使用分治范式(Strassen算法的前一步)。我的问题不是关于不同的方法,而是更多的是关于嵌套列表的切片运算符,这些嵌套列表可以不使用numpy。切片嵌套列表的nxn块是可能的,但很混乱。您将创建新的列表,没有任何类型的视图。
[row[:i]用于列表中的row[:i]
arr = np.array(z)
arr = arr.transpose(0,2,1,3).reshape(4,4)