【问题标题】:How to construct a symmetric matrix efficiently?如何有效地构造对称矩阵?
【发布时间】:2019-02-25 16:14:14
【问题描述】:

我想构造一个对称矩阵。

r=np.arange(0,11)
k=np.zeros((11,11))

for i in r:
    k[i]=np.arange(i,i-11,-1)

如何摆脱for循环,更高效地构造矩阵?

【问题讨论】:

  • 迭代器内部的输入和操作函数都是范围数组吗?此外,它看起来也不是对称的。
  • 是的,其实我是在问第一步构建对称矩阵。
  • scipy.linalg.toeplitz

标签: python arrays loops numpy matrix


【解决方案1】:

你可以这样做:

k = np.arange(0, 11)[:, np.newaxis] + np.arange(0, -11, -1)
print(k)

输出:

[[  0  -1  -2  -3  -4  -5  -6  -7  -8  -9 -10]
 [  1   0  -1  -2  -3  -4  -5  -6  -7  -8  -9]
 [  2   1   0  -1  -2  -3  -4  -5  -6  -7  -8]
 [  3   2   1   0  -1  -2  -3  -4  -5  -6  -7]
 [  4   3   2   1   0  -1  -2  -3  -4  -5  -6]
 [  5   4   3   2   1   0  -1  -2  -3  -4  -5]
 [  6   5   4   3   2   1   0  -1  -2  -3  -4]
 [  7   6   5   4   3   2   1   0  -1  -2  -3]
 [  8   7   6   5   4   3   2   1   0  -1  -2]
 [  9   8   7   6   5   4   3   2   1   0  -1]
 [ 10   9   8   7   6   5   4   3   2   1   0]]

但是请注意,这个矩阵不是对称的,而是antisymmetric

另一个获得相同结果但使用更少内存的更高级选项是创建一个数字从 10 到 -10 的数组,并在每一行上“滚动”它:

import numpy as np

def make_matrix(n):
    r = np.arange(n, -(n + 1), -1)
    s, = r.strides
    m = np.ndarray(shape=(n + 1, n + 1),
                   dtype=r.dtype,
                   buffer=r.data,
                   offset=s * n,
                   strides=(-s, s),
                   order='C')
    # Avoid writing since it is not a contiguous array
    m.flags.writeable = False
    return m

print(make_matrix(10))
# Same output

这仅占用第一个数组的内存,而不是连续矩阵的二次大小。

编辑:

如果你想创建一个对称矩阵,你可以取绝对值:

k = np.abs(np.arange(0, 11)[:, np.newaxis] + np.arange(0, -11, -1))

或者你可以像这样稍微修改上面的函数:

import numpy as np

def make_matrix(n):
    a = np.arange(n + 1)
    r = np.concatenate([a[::-1], a[1:]])
    s, = r.strides
    m = np.ndarray(shape=(n + 1, n + 1),
                   dtype=r.dtype,
                   buffer=r.data,
                   offset=s * n,
                   strides=(-s, s),
                   order='C')
    m.flags.writeable = False
    return m

print(make_matrix(10))

输出:

[[ 0  1  2  3  4  5  6  7  8  9 10]
 [ 1  0  1  2  3  4  5  6  7  8  9]
 [ 2  1  0  1  2  3  4  5  6  7  8]
 [ 3  2  1  0  1  2  3  4  5  6  7]
 [ 4  3  2  1  0  1  2  3  4  5  6]
 [ 5  4  3  2  1  0  1  2  3  4  5]
 [ 6  5  4  3  2  1  0  1  2  3  4]
 [ 7  6  5  4  3  2  1  0  1  2  3]
 [ 8  7  6  5  4  3  2  1  0  1  2]
 [ 9  8  7  6  5  4  3  2  1  0  1]
 [10  9  8  7  6  5  4  3  2  1  0]]

关于性能,在这种情况下,您有几个连续与非连续的测试:

import numpy as np

def make_matrix_cont(n):
    return np.abs(np.arange(0, n + 1)[:, np.newaxis] + np.arange(0, -(n + 1), -1))

def make_matrix_noncont(n):
    a = np.arange(n + 1)
    r = np.concatenate([a[::-1], a[1:]])
    s, = r.strides
    m = np.ndarray(shape=(n + 1, n + 1), dtype=r.dtype, buffer=r.data, offset=s * n, strides=(-s, s), order='C')
    m.flags.writeable = False
    return m

n = 1000
k_cont = make_matrix_cont(n)
k_noncont = make_matrix_noncont(n)
print(np.all(k_cont == k_noncont))
# True

%timeit make_matrix_cont(n)
# 3.48 ms ± 42.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit make_matrix_noncont(n)
# 5.2 µs ± 11.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit k_cont.sum()
# 317 µs ± 4.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit k_noncont.sum()
# 370 µs ± 1.59 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit k_cont @ k_cont
# 313 ms ± 3.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit k_noncont @ k_noncont
# 417 ms ± 1.44 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

因此,除了占用更少空间外,非连续矩阵的创建速度要快得多,但对其元素求和会慢一些,而矩阵乘法会更多。

【讨论】:

  • Thx,除了使用abs值的方式,矩阵可以用另一种方式构造成对称矩阵吗?
  • @kinderchan 我不确定你所说的“绝对值”是什么意思,我在这里没有使用绝对值。另外,您对“矩阵能否以另一种方式构造为对称矩阵”有何看法?你的意思是不同的方法,还是你想要不同的结果?我添加了另一种更高级的方法来使用更少的内存获得相同的结果(如果你想创建一个非常大的矩阵)。
  • 我的意思是使用np.abs 来获得对称的。使用更少的内存更有效,对吗?
  • @kinderchan 更少的内存在内存方面更有效,在计算时间方面,对于可以使用非连续数组完成的操作,它应该是相似的(由于更好的缓存?我不确定,不是这方面的专家)。如果该操作需要一个连续的数组,它将被转换,然后与其他情况相同。您是想直接创建对称矩阵(使用 abs 值)还是特别想要先反对称矩阵,然后再创建对称矩阵?
  • scipy.linalg.toeplitz 可能不是最有效但可能最方便的生成此类矩阵的方法。 linalg.toeplitz(np.arange(11))
【解决方案2】:

您可以在一个衬里中创建您的数组。

np.fromfunction(lambda r,c: r-c, (11,11))

结果:

array([[  0.,  -1.,  -2.,  -3.,  -4.,  -5.,  -6.,  -7.,  -8.,  -9., -10.],
       [  1.,   0.,  -1.,  -2.,  -3.,  -4.,  -5.,  -6.,  -7.,  -8.,  -9.],
       [  2.,   1.,   0.,  -1.,  -2.,  -3.,  -4.,  -5.,  -6.,  -7.,  -8.],
       [  3.,   2.,   1.,   0.,  -1.,  -2.,  -3.,  -4.,  -5.,  -6.,  -7.],
       [  4.,   3.,   2.,   1.,   0.,  -1.,  -2.,  -3.,  -4.,  -5.,  -6.],
       [  5.,   4.,   3.,   2.,   1.,   0.,  -1.,  -2.,  -3.,  -4.,  -5.],
       [  6.,   5.,   4.,   3.,   2.,   1.,   0.,  -1.,  -2.,  -3.,  -4.],
       [  7.,   6.,   5.,   4.,   3.,   2.,   1.,   0.,  -1.,  -2.,  -3.],
       [  8.,   7.,   6.,   5.,   4.,   3.,   2.,   1.,   0.,  -1.,  -2.],
       [  9.,   8.,   7.,   6.,   5.,   4.,   3.,   2.,   1.,   0.,  -1.],
       [ 10.,   9.,   8.,   7.,   6.,   5.,   4.,   3.,   2.,   1.,   0.]])

数组中的每个单元格都是行号减去列号。第一个参数是一个以行和列作为参数的函数。二是需要的形状。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-20
    • 1970-01-01
    • 2014-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多