2014-10-09 91 views
24

我使用Python创建对称矩阵/阵列,NumPy的,利用标准方法意外的结果:与+ =上与NumPy阵列

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 

OK ....

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数组?

+13

这个问题需要正确的标题。 – plaes 2014-10-09 12:18:33

+0

@AndrewJaffe这是Python 3.4.1上的Numpy 1.9,分布在Anaconda。 – jeffalstott 2014-10-09 12:34:16

+0

@jeffalstott是的,我误解了这个问题 - 我也看到了这种行为。 – 2014-10-09 12:37:47

回答

24

transpose operation返回数组的视图,这意味着没有新的数组被分配。这反过来又意味着你正在读取和修改数组。很难说出为什么某些尺寸或结果的某些区域有效,但最有可能与numpy如何处理阵列添加(可能会创建子矩阵的副本)和/或阵列视图有关(可能适用于它创建的小尺寸一个新的数组)。

x = x + x.T操作工作,因为那里你正在创建一个新的数组,然后分配给x,当然。

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 

解决了这个问题,但是这样你就没有任何空间利益。

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 

这当然是一个危险的事情,依靠正确性,因为它确实是一个实现细节可能会改变。