如何加速python循环

时间:2012-01-07 15:34:32

标签: python performance nested-loops

我看了几个网站上的几个讨论,但没有一个给我一个解决方案。 这段代码运行时间超过5秒:

for i in xrange(100000000):
  pass

我正在研究整数优化问题,我必须使用 O(n log n)算法编辑:一个O(n²/ 4)算法,其中n代表所有矩阵'项,即在下面的代码中,n * m = 10000.因此,对于具有10000个元素的矩阵100 * 100,它将导致近25000000次迭代...... 。 它的代码可以总结如下:

m = 100
n = 100
for i in xrange(m):
  for j in xrange(n):
    for i2 in xrange(i + 1, m):
      for j2 in xrange(j + 1, n):
        if myarray[i][j] == myarray[i2][j2] and myarray[i2][j] == myarray[i][j2]:
          return [i, j], [i2, j2]

我应该放弃Python并返回Java或C?

我使用Python 2.7并且Psyco不可用。 PyPy不支持Tkinter开箱即用,我正在使用Tkinter。

那么,他们会提高循环速度吗?还有其他解决方案吗?

5 个答案:

答案 0 :(得分:15)

不是最漂亮的编码风格,但绝望的时候需要绝望的编码。尝试将嵌套的嵌套循环转换为一个大的生成器表达式:

try:
    i,j,i2,j2 = ((i,j,i2,j2)
        for i in xrange(m)
          for j in xrange(n)
            for i2 in xrange(i + 1, m)
              for j2 in xrange(j + 1, n)
                if myarray[i][j] == myarray[i2][j2] and 
                    myarray[i2][j] == myarray[i][j2]).next()
    return [i,j],[i2,j2]
except StopIteration:
    return None

已更新为使用内置nextproduct以及Py3 range而不是xrange

from itertools import product
match = next(((i,j,i2,j2)
    for i, j in product(range(m), range(n))
        for i2, j2 in product(range(i+1, m), range(j+1, n))
            if myarray[i][j] == myarray[i2][j2] and 
                myarray[i2][j] == myarray[i][j2]), None)
if match is not None:
    i,j,i2,j2 = match
    return [i,j],[i2,j2]
else:
    return None

答案 1 :(得分:11)

您仍然可以使用python表示法,并使用Cython项目获得C的速度。 第一步是在C循环中转换循环:它是通过键入循环中使用的所有变量自动完成的:

cdef int m = 100
cdef int n = 100
cdef int i, j, i2, j2
for i in xrange(m):
  for j in xrange(n):
    for i2 in xrange(i + 1, m):
      for j2 in xrange(j + 1, n):

对于下一部分,如果myarray是纯C数组,那么没有丰富的python comparaison或数组访问会更好。使用你的python数组,你可以通过本机比较来删除丰富的比较(如果你的数组有两倍):

        cdef double a, b, c, d
        a = myarray[i][j]
        b = myarray[i2][j2]
        c = myarray[i2][j]
        d = myarray[i][j2]

        if a == b and c == d:
          return [i, j], [i2, j2]

您可以通过运行cython -a yourfile.pyx,然后打开yourfile.html生成来查看优化的进展情况。您将看到Cython如何优化您的代码,并消除了Python开销。

答案 2 :(得分:2)

很抱歉告诉您,但这不是优化问题。无论您选择哪种语言或实现,此算法在最差和平均情况下都不是O(n*log n)。您可能希望阅读how the Big-O notation works并开发出更好的算法。

答案 3 :(得分:1)

抱歉生成器expr不起作用。这是一个不同的方案,首先计算所有类似的值,然后查找矩形组:

from collections import defaultdict
from itertools import permutations
def f3(myarray):
    tally = defaultdict(list)
    for i,row in enumerate(myarray):
        for j,n in enumerate(row):
            tally[n].append((i,j))

    # look for rectangular quads in each list
    for k,v in tally.items():
        for quad in permutations(v,4):
            # sort quad so that we can get rectangle corners 
            quad = sorted(quad)
            (i1,j1),(i2,j2) = quad[0], quad[-1]

            # slice out opposite corners to see if we have a rectangle
            others = quad[1:3]

            if [(i1,j2),(i2,j1)] == others:
                return [i1,j1],[i2,j2]

答案 4 :(得分:1)

我在你的代码中看了好几次,如果我说对了,你用两个不同的角标记你的矩形。我很抱歉,如果我的答案更多的是澄清你的立场,那么就是一个真正的答案。

<强>答-部分:: 如果您正在寻找合适的算法,请考虑使用扫描线算法。我为“最大的矩形解决方案”找到了一个示例here @SO

我的问题是,你到底在寻找什么?

  1. 解决for-loop困境的最佳方法
  2. 算法的最佳语言
  3. 查找矩形的更快算法
  4. 我还必须指出,保罗和你的解决方案会产生不同的结果,因为保罗认为角落标有相同的值而你假设,角落是通过两个不同的值标记的!

    我花时间和自由用一个丑陋的c&amp; p脚本来说明它: 它通过创建2D字段并将其填充来比较两种算法 string.lowercase chars并将角转换为大写字符。

    from random import choice
    from string import lowercase
    from collections import defaultdict
    from itertools import permutations
    from copy import deepcopy
    
    m,n = 20, 20
    myarray = [[choice(lowercase) for x in xrange(m)] for y in xrange(n)]
    
    def markcorners(i,j,i2,j2):
        myarray[i][j] = myarray[i][j].upper()
        myarray[i2][j] = myarray[i2][j].upper()
        myarray[i2][j2] = myarray[i2][j2].upper()
        myarray[i][j2] = myarray[i][j2].upper()
    
    def printrect():
        for row in myarray:
            print ''.join(row)
        print '*'*m
    
    def paul_algo():
        tally = defaultdict(list)
        for i,row in enumerate(myarray):
            for j,n in enumerate(row):
                tally[n].append((i,j))
    
        # look for rectangular quads in each list
        for k,v in tally.items():
            for quad in permutations(v,4):
                # sort quad so that we can get rectangle corners 
                quad = sorted(quad)
                (i1,j1),(i2,j2) = quad[0], quad[-1]
    
                # slice out opposite corners to see if we have a rectangle
                others = quad[1:3]
    
                if [(i1,j2),(i2,j1)] == others:
                    markcorners(i1,j1,i2,j2)
    
    def xavier_algo():   
        for i in xrange(m):
            for j in xrange(n):
                for i2 in xrange(i + 1, m):
                    for j2 in xrange(j + 1, n):
                        if myarray[i][j] == myarray[i2][j2] and myarray[i2][j] == myarray[i][j2]:
                            markcorners(i,j,i2,j2)
    
    savearray = deepcopy(myarray)
    printrect()
    
    xavier_algo()
    printrect()
    
    myarray = deepcopy(savearray)
    paul_algo()
    printrect()