所有可能组合的最小二乘差异的高性能计算(n列表)

时间:2012-12-05 19:48:39

标签: python performance dictionary combinations least-squares

我正在寻找一种非常有效的方法来计算n个列表中的所有可能组合,然后保持组合中最小的最小二乘差异。

我已经有了一个代码可以做到这一点,但是当它达到数百万个组合时,事情变得缓慢。

candidates_len 包含长度列表,[[500,490,510,600] [300,490,520] [305,497,515]] candidates_name 包含一个名单列表,例如[[' a',' b',' c',' d& #39;] [' mi',' mu'' ma'] [' pi',' pu' ,']]

两个列表都有 n 列表。

#    Creating the possible combinations and store the lists of lengths in vector r
r=[[]]
for x in candidates_len:
    r = [ i + [y] for y in x for i in r ]
#Storing the names of the combinations and store the lists of identifiers in vector z
z=[[]]
for x in candidates_name:
    z = [ i + [y] for y in x for i in z ]          
#Calculating distances and storing the minimum one
min_index = 0
min_dist = 0
list_best = []
for index, item in enumerate(r):
    n = 0
    dist = 0
    while n < len(candidates_len):
        for i in range(n,len(candidates_len)):
            dist = dist + (item[n]-item[i])**2
        n=n+1
    if index==0:
            min_dist = dist
            min_index = index
            list_best.append(item)
    elif dist < min_dist:
        min_dist = dist
        min_index = index
        list_best = []
        list_best.append(z[index])
least_combination = min_index

一个很难的案例: http://pastebin.com/BkVQTQWK

以下是一些测试时间。大约一分钟左右就好了。我不知道它是否可能。

combinations time(s)
77760   1.255663
41184   1.580333
69120   6.214786
8960   1.131834
537600  14.855361
89100   1.264126
16384   3.247404
4199040 666.853284
226800   3.935878
9289728 679.064149

2 个答案:

答案 0 :(得分:5)

我在这里首先想到的是,你花了很多时间来建立你不需要的列表。至少,废弃它们会使事情变得更简单,但不能保证它会让事情变得更快:

r = itertools.product(*candidates_len)
z = itertools.product(*candidates_name)

min_dist = None
for item, names in itertools.izip(r, z):
  dist = 0
  for n in range(len(item)):
    for i in range(n, len(item)):
      dist += (item[n]-item[i])**2
  if min_dist is None or dist < min_dist:
    min_dist = dist
    best = item, names

print(best)

使用您的测试数据,显式列表占用了数十亿字节的内存。我不知道多少 - 我的可怜的4GB笔记本电脑在它生成z列表之前就已经进入交换捶打地狱,并且一切都变慢了。 itertools整个操作所花费的时间少于没有它的设置部分...在具有16GB RAM的机器上可能不是这样,但是,如果你不需要,为什么还要使用内存它?

我的下一个想法是,你所做的只是在一堆数组上计算LSD。你有大量的小阵列吗?如果是这样,你可以解除它们(例如,用无填充)和numpy整个事情吗?另一方面,如果它是一个大数组的数组,你可能想要一个numpy数组的列表(或者,如上所述,迭代器),所以至少你可以向量化一维。

无论哪种方式,矢量化都是优化涉及大数组上简单操作的任何事情的关键,numpy通常比任何专家C ++更好地进行矢量化 - 并且Fortran和特定于平台的程序集编码器可能会手动完成。

如果没有仔细考虑代码或试图深入理解算法,我的第一次尝试就是生成r作为序列(如上面的代码中所示)但numpy行向量(类似matrix(x, dtype=int) for x in itertools.product(*candidates_len))。然后你可以通过item计算每个item - item.T的差异,然后总结下三角的平方(我必须查找它以找出怎么做)。然后,你可以通过找出一种只计算下三角形的方法来进一步改善性能。这方面的典型技巧是弄清楚如何将低三角形和作为矢量化操作的一部分进入对角线,然后你只需提取对角线,但这并不总是合适的。有关如何在不创建显式矩阵的情况下对内循环进行矢量化的一些想法,请参阅the broadcasting docs。最后,看看是否有一种方法可以从整个事物中创建一个3D数组(这可能意味着将各个项目填充到固定的宽度),然后矢量化整个操作。 (内存使用不会差一点,因为numpy只需要为每个值分配4个字节而不是整个PyObject ...但是它可能仍然很糟糕,你输的比你多如果这有点模糊,很抱歉。但希望这足以让你开始实验。

另一个想法是你可以将它并行化。任何有足够内存来处理大量列表的机器,我愿意打赌它至少有4个内核。而且你有一系列完全独立的操作,这是世界上最容易并行化的操作。作为第一步,创建一个multiprocessing.Pool,并使外部循环将作业提交到池中,而不是直接进行工作。您可能会发现作业太小,因此您在开销中淹没,但随后您可以批量处理每个N项(明确地,或查看grouper中的itertools食谱docs),并让这个工作“循环这些N个项目,然后返回带有最小LSD的项目”。 (可能需要一些调整才能找到最佳N.)您甚至可以与顶级numpy一起执行此操作,将巨型阵列沿x轴分割成块并将其作为作业进行耕种。 / p>

还有一个想法:你的算法以N * M的乘积开始,每个元素的长度为N.然后,对于每个元素,你将它循环两次。因此,最好的性能将是O(N ^ 3 * M)。这真的是正确的算法吗?如果是这样,您实际上是否从算法中获得N ^ 3 * M的性能?如果任何一个问题的答案都是否定的,那么您不应该尝试对其进行微观优化。只有当你真正得到最有效的算法,编码正确时,才值得做一些事情,比如矢量化,避免过多的工作,将紧密的循环移动到C ++和Fortran等等。否则,你只会回来说“但是当我进入上一次试运行的4倍时它仍然会爆炸“。

答案 1 :(得分:2)

我要做的第一件事就是在Numpy数组中尽可能多地使用它。 Numpy中基于数组的操作以或多或少的C速度执行。看起来很多这样的事情可以在Numpy开始......

如果那不能让你的血液流动,那么我会分析代码,并在Cython中为bottel颈部创建一个函数。假设你可以在列表/数组上放置静态类型,如果你想留在Pythonic世界,Cython可能是你最好的选择。我亲眼看到使用Cython的一些瓶颈加速100倍。

以下是其文档中an image convolution with Cython的示例。