我使用标准方法使用NumPy在Python中创建对称矩阵/数组:
x = rand(500,500)
x = (x+x.T)
all(x==x.T)
> True
现在让我们聪明一点:
x = rand(500,500)
x += x.T
all(x==x.T)
> False
等等,什么?
x==x.T
> array([[ True, True, True, ..., False, False, False],
[ True, True, True, ..., False, False, False],
[ True, True, True, ..., False, False, False],
...,
[False, False, False, ..., True, True, True],
[False, False, False, ..., True, True, True],
[False, False, False, ..., True, True, True]], dtype=bool)
左上和右下段是对称的。如果我选择较小的阵列怎么办?
x = rand(50,50)
x += x.T
all(x==x.T)
> True
行...
x = rand(90,90)
x += x.T
all(x==x.T)
> True
x = rand(91,91)
x += x.T
all(x==x.T)
> False
只是为了确定......
x = rand(91,91)
x = (x+x.T)
all(x==x.T)
> True
这是一个错误,还是我要学习关于+=
和NumPy数组的疯狂事情?
答案 0 :(得分:24)
transpose
operation返回数组的视图,这意味着没有分配新数组。反过来,这意味着您正在同时读取和修改数组。很难说为什么某些大小或结果的某些区域可以工作,但很可能它与numpy如何处理数组加法(可能是副本的子矩阵)和/或数组视图(可能是为了创建的小尺寸)有关一个新阵列。)
x = x + x.T
操作有效,因为您正在创建新数组,然后分配给x
。
答案 1 :(得分:20)
其他人提到的实施细节称为缓冲。您可以在the docs on array iteration中了解有关它的更多信息。
如果你更详细地看一下你失败的例子:
>>> a = np.random.rand(91, 91)
>>> a += a.T
>>> a[:5, -1]
array([ 0.83818399, 1.06489316, 1.23675312, 0.00379798, 1.08967428])
>>> a[-1, :5]
array([ 0.83818399, 1.06489316, 1.75091827, 0.00416305, 1.76315071])
所以第一个错误的值是90*91 + 2 = 8192
,毫不奇怪,这是我们得到的:
>>> np.getbufsize()
8192
我们也可以将它设置得更高,然后:
>>> np.setbufsize(16384) # Must be a multiple of 16
8192 # returns the previous buffer size
>>> a = np.random.rand(91, 91)
>>> a += a.T
>>> np.all(a == a.T)
True
虽然现在:
>>> a = np.random.rand(129, 129)
>>> a += a.T
>>> np.all(a == a.T)
False
这对于正确性来说当然是一件危险的事情,因为它确实是一个可以改变的实现细节。
答案 2 :(得分:4)
问题是添加不是“立即”; x.T
是x
的视图,因此当您开始添加x
的每个元素时,x.T
会发生变异。这会混淆以后的添加。
它适用于(91, 91)
以下尺寸的事实几乎肯定是一个实现细节。使用
x = numpy.random.rand(1000, 1000)
x += x.T.copy()
numpy.all(x==x.T)
#>>> True
解决了这个问题,但是你没有任何空间利益。