如何预分配一个列表的列表?

12

我正在使用以下代码创建一个列表的列表:

zeroArray = [0]*Np
zeroMatrix = []
for i in range(Np):
    zeroMatrix.append(zeroArray[:])

有没有更高效的方法?我希望类似于zeroArray = [0]*Np; zeroMat = zeroArray*Np这样的东西,但是找不到类似的东西。


这是一个用于表示点数的变量。 - Charles L.
2
如果你在进行数字工作,我强烈推荐使用一个数字包。 - detly
4个回答

11

也许你应该考虑使用NumPy,它专门用于数字计算。这是目前最快的方法,不包括导入语句:

import numpy
Np = 80
zeroMatrix = numpy.zeros((Np, Np))

时间:

>python -m timeit -s "import numpy; Np = 80" "zeroMatrix = numpy.zeros((Np, Np))"
100000 loops, best of 3: 4.36 usec per loop

>python -m timeit -s "Np = 80" "zeroArray = [0]*Np" "zeroMatrix = [None] * Np" "for i in range(Np):" "  zeroMatrix[i] = zeroArray[:]"
10000 loops, best of 3: 62.5 usec per loop

>python -m timeit -s "Np = 80" "zeroMatrix = [[0] * Np for i in range (Np)]"
10000 loops, best of 3: 77.5 usec per loop

>python -m timeit -s "Np = 80" "zeroMatrix = [[0 for _ in range(Np)] for _ in range(Np)]"
1000 loops, best of 3: 474 usec per loop

3
+1 当然,一旦您设置了数组,NumPy拥有一组强大的数组操作,这些操作比为Python列表编写自己的操作要快得多。 - JoshAdel

10

你可以这样做:

zeroMatrix = [[0] * Np for i in range(Np)]

更新: 好吧,如果我们要将其变成一场比赛,我在我的电脑上找到了比Omnifarious'方法更快的东西。当然,这并不能打败NumPy;但无论如何,这都是学术性的,对吧?我的意思是,我们谈论的是微秒级别的问题。

我认为这种方法可行是因为它避免了使用append 预分配zeroMatrix

zeroArray = [0] * Np
zeroMatrix = [zeroArray[:] for i in range(Np)]

我的测试结果:

$ python -m timeit -s "Np = 80" "zeroMatrix = [[0] * Np for i in range(Np)]"
1000 loops, best of 3: 200 usec per loop
$ python -m timeit -s "Np = 80" "zeroArray = [0] * Np" "zeroMatrix = [None] * Np" "for i in range(Np):" "    zeroMatrix[i] = zeroArray[:]"
10000 loops, best of 3: 171 usec per loop
$ python -m timeit -s "Np = 80" "zeroArray = [0] * Np" "zeroMatrix = [zeroArray[:] for i in range(Np)]"
10000 loops, best of 3: 165 usec per loop

1
第二个(如果我没记错)会使zeroMatrix的每个元素成为同一个可变列表的副本,因此zeroMatrix[0][2] += 1将影响所有的zeroMatrix[n][2]元素。(除非他们已经改变了Python 3的这种行为。我真的需要升级了...)编辑:算了,你已经把它删掉了。 - Chris Lutz
@Chris Lutz,是的,我发帖后几乎立刻就想到了!不过还是谢谢你。 - senderle
1
@Charles - 不,它不是这样的。senderle 曾经有过那段代码,但后来将其删除了,因为它是错误的。请参考我之前的评论。 - Chris Lutz
1
@Charles L.:那样做是不行的。一旦你设置了矩阵中的一个元素,该元素所在的整列看起来都被设置为相同的值。 - Omnifarious
1
我测试了一下,你的版本始终比我的版本稍微慢一些。我怀疑哪个版本更好取决于Python版本和/或硬件平台。 - Omnifarious
显示剩余2条评论

5
这样可能会更高效一些:
zeroArray = [0]*Np
zeroMatrix = [None] * Np
for i in range(Np):
    zeroMatrix[i] = zeroArray[:]

你真正想要的方式不能按照你的希望运作。这是因为如果使用“*”创建列表元素的Np个副本,你将得到指向同一对象的Np个引用。对于数字0来说这并不是什么大问题,因为当你给它添加任何值时,你只会获得一个新数字。但对于列表来说,你最终会得到一个矩阵,在该矩阵中,一旦更改某一行的任何元素,整个列也会随之更改。
这种方法是目前提到的第二快的方法。
$ python3 -m timeit -s 'Np = 80' 'zeroArray = [0]*Np
zeroMatrix = [None] * Np
for i in range(Np):
    zeroMatrix[i] = zeroArray[:]'
10000 loops, best of 3: 72.8 usec per loop

$ python3 -m timeit -s 'Np = 80' 'zeroMatrix = [[0] * Np for i in range(Np)]'
10000 loops, best of 3: 85 usec per loop

$ python3 -m timeit -s 'Np = 80' 'zeroMatrix = [[0 for _ in range(Np)] for _ in range(Np)]'
1000 loops, best of 3: 566 usec per loop

由于我系统上没有Python3的numpy包,因此无法对基于numpy的解决方案进行时间测试。但是它明显比其他方案快得多。


好吧,试图制作绝对最快的程序就到此为止了。这真的很恼人。 - Omnifarious
@Omnifarious - 我还是赢了 :P - detly
@detly - 原来如此。我对numpy并不是很熟悉。 :-) - Omnifarious
@Omnifarious - 我很喜欢它,因为我也许再也不必使用Matlab了。 - detly
@Omnifarious - 不要这样做。就是...不要这样做。 - detly
显示剩余3条评论

2
也许这正是你想要的?
zeroMatrix = [[0 for _ in range(Np)] for _ in range(Np)]

我不确定这是否会提高性能(像往常一样进行分析),但我不知道你所说的“有效”是什么意思。除了避免使用list.append之外。


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