web-dev-qa-db-ja.com

高速化する方法python loop

私はいくつかのサイトでいくつかの議論を見て、それらのどれも私に解決策を与えませんでした。このコードは、実行に5秒以上かかります。

for i in xrange(100000000):
  pass

私は整数の最適化問題に取り組んでおり、O(n log n)アルゴリズムedit:an 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を使用しています。

では、ループ速度を向上させるでしょうか?他の解決策はありますか?

18
s_xavier

かわいらしいコーディングスタイルではありませんが、絶望的な時代には必死のコーディングが必要です。ネストされたネストされたループを1つの大きなジェネレータ式に変えてみてください。

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、およびrangeの代わりにPy3 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
16
PaulMcG

引き続き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比較も配列アクセスもない場合はさらに優れています。python配列、ネイティブ比較を行うことで豊富な比較を削除できます(配列にdoubleがある場合)。

        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オーバーヘッドを削除したか)がわかります。

11
tito

申し訳ありませんが、最適化の問題ではありません。選択する言語や実装に関係なく、このアルゴリズムは最悪の場合や平均的な場合ではO(n*log n)ではありません。 Big-O表記の仕組み を読んで、より優れたアルゴリズムを開発することをお勧めします。

2
Niklas B.

ジェネレータ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]
1
PaulMcG

私はあなたのコード全体を何度か調べましたが、正しければ、2つの異なるコーナーマーカーで長方形をマークしています。私の答えがあなたの立場を明確にするためのコメントである場合は本当に申し訳ありません。

Answer-Part::適切なアルゴリズムを探している場合は、スキャンラインアルゴリズムを検討してください。 「最大の長方形の解」の例 ここでは@SO を見つけました。

あなたへの私の質問は、あなたは本当に何を探しているのですか?

  1. forループのジレンマを解決する最良の方法
  2. アルゴリズムに最適な言語
  3. 長方形を見つけるためのより高速なアルゴリズム

ポールとあなたの解決策は異なる結果を生み出すことも指摘しなければなりません。ポールはコーナーが同じ値でマークされていると想定し、コーナーは2つの異なる値でマークされていると想定しているためです。

醜いc&pスクリプトでそれを説明するのに時間と自由を費やしました。2Dフィールドを作成して両方のアルゴリズムを比較し、string.lowercase文字を入力して、角を大文字にします。

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()
1
Don Question