这意味着我希望有一个对象支持列表的两个(非常)基本操作:获取某个索引(1)中的对象并更改其值(2)。
他们没有解决我的问题,因为他们的所有解决方案都太慢了:在我的电脑array.array('i',(0,)*10 ** 8)
导致错误(笑); [0 for _ in range(10**8)]
花了大约15秒钟(哇!); [0] * 10 ** 8
花了2.3秒; [None] * 10 ** 8
花了1.8秒; (1.8秒可能会更快......)
我尝试使用ctypes
模块
from ctypes import c_int
array = (c_int * 10 ** 8)()
上面的代码只花了0.7秒......但有没有办法让它更快?除了快速,它还有一些缺点:
我真的可以做我要问的事吗?是否有更快的方式而不是使用ctypes
模块?如果是这样,请确保使用“内置”/“预安装”模块。
我正在使用python进行竞争性编程,大多数口译/评委都不允许使用外部库。
我可以看到许多答案使用array
模块的array
函数。他们都使用'i'来指定我们想要存储整数。是否可以创建一个类并创建一个包含它的`array.array'?例如:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# make array.array object with all indexes containing a Point with atributes x and y with value 0
# an example with a list of what I want to do is this:
# l = [Point(0, 0) for _ in range(10**3)]
答案 0 :(得分:4)
array.array('i',(0,) * 10**8)
导致错误(lol)
你没有指定你得到的错误 - 这对我有用,虽然它不是很快,因为它构建了一个中间元组并立即丢弃它。使用Python的内置类型,array.array
可能会产生最佳性能,只要你避免使用元组:
a = array.array('i', (0,)) * 10**8
上面的代码只花了0.7秒......但有没有办法让它更快?
如果您不允许创建或导入C扩展名,则很难超越array.array
。在我几年前的机器上,上面需要0.6秒。您可以通过增加初始数组的大小来进一步优化它。例如,这会产生相同的结果,但几乎快3倍(!):
# 0.22 s
a = array.array('i', (0,) * 10) * 10**7
在我的机器上,以下版本效果最佳:
# 0.19 s
a = array.array('i', (0,) * 100) * 10**6
进一步增加初始数组大小并没有帮助,很快就会开始降低性能。
为了提高效率,请考虑其他方法,例如惰性列表或为您的用例量身定制的完全不同的数据结构。鉴于竞争的背景,这可能是实际上正在寻求的。
但请注意,每种解决方案都会有不同的权衡。例如,像@KonstantinNikitin提供的一个惰性数组将非常有效地构建,但是用纯Python实现的__getitem__
和__setitem__
将比列表慢几个数量级。或array.array
。对您来说哪个更好,归结为您的计划中更频繁的操作,这取决于您的发现。
答案 1 :(得分:3)
我只使用numpy
模块,它支持快速数组操作。
例如,制作一个数字为0到10的数组** 8:
import numpy as np
import time
b = time.time()
a = np.linspace(0, 10**8, 10**8)
c = time.time()
print(c-b)
>>>0.5000154972076416
或者制作一个长度为10 ** 8的0数组:
b = time.time()
a = np.zeros(shape=(10**8,))
c = time.time()
print(c-b)
>>>0.0
numpy这么快的主要原因是因为它是用C实现的。
编辑:
如果您只想使用预安装的软件包,可以尝试使用array
软件包:
import array
import time
r = time.time()
a = array.array('i', [0]) * (10**8)
print(time.time()-r)
>>>0.15627217292785645
答案 2 :(得分:2)
我会说你可以尝试不同的方法:
1)numpy
。它确实是阵列的标准。它带来了跨越Python的成本< - >每个操作的C边界,但它实际上取决于您的任务。
x = numpy.array(10 ** 8)
timeit.timeit('x = numpy.array(10 ** 8)', 'import numpy', number=1)
4.195800283923745e-05
2)延迟初始化(如JavaScript数组)。
class LazyArray:
def __init__(self, size):
self.storage = {}
self.size = size
def check(self, i):
if i < 0 or i >= self.size:
raise RuntimeError()
def __getitem__(self, i):
self.check(i)
return self.storage.get(i, 0)
def __setitem__(self, i, value):
self.check(i)
self.storage[i] = value
x = LazyArray(10 ** 8)
x[10]
>> 0
x[10] = 5
x[10]
>> 0
答案 3 :(得分:2)
如果确实只想要这两个属性:
获取某个索引(1)中的对象并更改其值(2)
然后您可以使用collections.defaultdict
:
import collections
my_list = collections.defaultdict(lambda: 0)
相当快(~0.4μs):
$ python3 -m timeit -s 'import collections' 'collections.defaultdict(lambda: 0)'
1000000 loops, best of 3: 0.417 usec per loop
然而,实际使用它可能会比其他答案中提出的任何类型都慢一些。
答案 4 :(得分:0)
对于只需要0到255之间整数的情况,bytearray
个对象的创建速度非常快:
>>> timeit.timeit('bytearray(100000)', number=1000)
0.005567271093696036
>>> timeit.timeit('array.array("B", [0])*100000', 'import array', number=1000)
0.36631167401839093
>>> timeit.timeit('array.array("i", [0])*100000', 'import array', number=1000)
0.56494557472422
与array.array
不同,它直接将分配归零,而不是从用零初始化的对象进行复制。