如何高效地创建一个大的numpy数组

5

给定一个偶数n,我想制作一个3^(n/2)-1乘以n的2d-numpy数组。每行的前一半应该遍历所有可能的-1、0、1值,而后一半应该为零。然而,前一半不应该全是零。

这段代码几乎可以工作,但它包括了我不想要的全零行。

n = 4
M = [list(row) +[0]*(n/2) for row in itertools.product([-1,0,1], repeat = n/2)]
print np.array(M)

它提供了

[[-1 -1  0  0]
 [-1  0  0  0]
 [-1  1  0  0]
 [ 0 -1  0  0]
 [ 0  0  0  0]
 [ 0  1  0  0]
 [ 1 -1  0  0]
 [ 1  0  0  0]
 [ 1  1  0  0]]

有没有一种更少可怕,更高效利用时间和空间的方法来完成这个任务?最终 n 将是30,当我设置 n=30 时,3^15 只有14,348,907,但代码在我的8GB机器上使用了所有内存,并且需要很长时间。如何直接创建 numpy 数组而不是通过 itertoolslist 等方式?

数组中行的顺序是否重要? - J Richard Snape
@JRichardSnape 很好的问题。这很重要。 - Simd
1个回答

5
这将创建您的数组,而无需进行任何大型辅助内存分配:
n = 30
assert 2 * (n // 2) == n
rows = 3**(n//2)
cols = n

arr = np.zeros((rows, cols), dtype=int)
shape = (rows,)

source = np.array([-1, 0, 1], dtype=np.int)[:, None]

for col in range(n//2):
    shape = (-1, 3, shape[-1]//3,)
    col_view = arr[:, col]
    col_view.shape = shape
    col_view[:] = source

在我的笔记本电脑上完成大约需要10秒钟。这是基于此类似问题的伟大答案而松散设计的。
使用这种方法难以轻松地消除构建过程中的零值,因为它对算法的工作至关重要。您之后总是可以把它去掉:
arr = np.delete(arr, rows//2, axis=0)

但这样会分配一个新的数组并将内容复制到其中,然后丢弃旧的数组,因此您的内存需求会突然增加一倍。
无法想到任何使用Python或NumPy快速解决它的简单方法。

我使用笛卡尔积作为基数扩展技巧进行了原地版本的编写,将其分为两个步骤来处理零,但即使在你的代码中添加了np.delete之后,你的代码仍然比我的快大约2倍。 - DSM
在这种情况下,选择较小的dtype并使用缓存友好的排序确实有助于提高性能。因为你完全摆脱了Python的开销,唯一剩下的就是原始内存访问速度。 - user2379410
事实上,使结果数组成为Fortran顺序可能会对性能产生巨大的积极影响。 - Jaime

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接