2016-09-19 4 views
1

Python 2.7を使用して、以下の問題に取り組んでいます。私のコードを投稿し、それをより速く動かすためのさらにスマートなアイデアがあるのだろうか?最初にリストをソートし、ソートの振る舞いを利用するアイディアがあるかもしれないと思っていましたが、これまでのところ分かりません。私のコードはO(n^2)時間の複雑さです。A + B = C + Dを満たす値のインデックスを見つける

通報、整数のアレイAを考える

は、A、B、C & Dは、アレイ内の整数値であり、A + B = C + Dを満たす値のインデックスを見つけます。 quadruplesのすべての組み合わせを見つける。

コード、合計値、および合計が、そのような値をもたらす可能性インデックスである

from collections import defaultdict 

sumIndex = defaultdict(list) 

def buildIndex(numbers): 
    for i in range(len(numbers)): 
     for j in range(i+1,len(numbers)): 
      sumIndex[numbers[i]+numbers[j]].append((i,j)) 

def checkResult(): 
    for k,v in sumIndex.items(): 
     if len(v) > 1: 
      for i in v: 
       print k, i 

if __name__ == "__main__": 
    buildIndex([1,2,3,4]) 
    checkResult() 

出力

5 (0,3) 
5 (1,2) 
+3

codereview.stackexchange.comウェブサイトにもっと適しています。 –

+2

間違った出力を追加しました。実際の出力: '5(0、3) 5(1,2) ' –

+0

@GaneshMatkam、訂正ありがとう。更新しました。 –

答えて

2

場合を考えるのすべての要素配列は等しい。それでは、あらかじめ答えを知っていますが、単に結果を印刷するには、n*(n-1)/2という数のペアがあるので、時間はO(n^2)になります。だから私はこの問題のためにO(n^2)よりも複雑なアプローチがないと言うのは安全だと思います。

+0

sudomakeinstall2さん、ありがとうございますあなたの返事に投票してください。ソートをしても何か改善があると思いますか(たとえまだO(n^2)であっても) –

+1

@LinMa指定された合計を持つペア並べ替えやバイナリ検索の恩恵を受けることができるいくつかのメソッドがありましたが、すべての可能な合計を持つすべてのペアを探したいので、既存のコードの改善について考えることはできません。 – Tempux

+0

sudomakeinstall2に感謝します。 ? –

1

はい、O(n^2)よりも複雑な方法で行うことができます。アルゴは:

  1. 複製配列を作成すると、元の配列の要素のインデックスorigArr []を格納しているindexArr []と仮定します。
  2. origArr []を、複雑さO(nLogn)を持ついくつかのアルゴを使用して昇順にソートします。同様に、origArr []をソートしながらindexArr []をシャッフルします。
  3. ソートされた配列でペアを見つける必要があります。可能な組み合わせをすべて見つけるために2つのループを実行します。 origArr[i] + origArr[i + 1] = sum.
  4. sum <= origArr[n]ここで、nは最大要素である配列の最後の要素であるかどうかを検索します。またsum > origArr[n]の場合、他の組み合わせは使用できないため、内側のループと外側のループが解除されます。
  5. また、sum > origArr[j]の場合は、内側ループを破ることになります。その合計のために他の組み合わせはできません。

PS - 最悪のシナリオはO(n^2)です。

+0

アイデアをありがとう、あなたのコメントのためにあなたのコメントのために投票してください - "ソートされた配列のペアを見つけたら、すべての可能な組み合わせを見つける2つのループを実行します"、私はあなたのコードで同じ実装を意味すると思います私はあなたのコードを表示することができれば、たくさんのことを明確にすることは素晴らしいことでしょう。 –

+0

そしてあなたのためにurは「今、あなたはsum <= origArr [n]」を検索します。なぜなら、sumは2つの要素の加算であり、origArr [n]は1つの要素の最大値なので、これを行うのが安全かどうかはわかりません。あなたが 'sum <= 2 * origArr [n]'を意味するなら、間違いなく2つの要素の合計が他の要素よりも大きい可能性があります。とにかく、あなたのコードを表示することができれば、はっきりしています。 :) –

+1

はい、あなたの実装になりますが、私が言及したように、最悪の場合のシナリオはあなたと同じになります。私のループはいくつかの条件に基づいて壊れています。 ソートされた配列[7、9、11、14]を考えてみましょう。配列の最大値は14(n)です。あなたが7(i)+ 9(i + 1)= 15(合計)を考えれば、 15> 14のように、組み合わせることはできません。私が何かを逃している場合は、私を指示します。 –

1

より速く、よりPython的アプローチ用いitertools.combinations:

from collections import defaultdict 
from itertools import combinations 

def get_combos(l): 
    d = defaultdict(list) 
    for indices in combinations(range(len(l)),2): 
     d[(l[indices[0]] + l[indices[1]])].append(indices) 
    return {k:v for k,v in d.items() if len(v) > 1} 


タイミング

           OP  this 
len(l)=4, min(repeat=100, number=10000) | 0.09334 | 0.08050 
len(l)=50, min(repeat=10, number=100) | 0.08689 | 0.08996 
len(l)=500, min(repeat=10, number=10) | 0.64974 | 0.59553 
len(l)=1000, min(repeat=3, number=3) | 1.01559 | 0.83494 
len(l)=5000, min(repeat=3, number=1) | 10.26168 | 8.92959 

タイミングコード

結果
from collections import defaultdict 
from itertools import combinations 
from random import randint 
from timeit import repeat 


def lin_get_combos(l): 
    sumIndex = defaultdict(list) 
    for i in range(len(l)): 
     for j in range(i+1,len(l)): 
      sumIndex[l[i]+l[j]].append((i,j)) 
    return {k:v for k,v in sumIndex.items() if len(v) > 1} 

def craig_get_combos(l): 
    d = defaultdict(list) 
    for indices in combinations(range(len(l)),2): 
     d[(l[indices[0]] + l[indices[1]])].append(indices) 
    return {k:v for k,v in d.items() if len(v) > 1} 

l = [] 
for _ in range(4): 
    l.append(randint(0,1000)) 
t1 = min(repeat(stmt='lin_get_combos(l)', setup='from __main__ import lin_get_combos, l', repeat=100, number=10000)) 
t2 = min(repeat(stmt='craig_get_combos(l)', setup='from __main__ import craig_get_combos, l', repeat= 100, number=10000)) 
print '%0.5f, %0.5f' % (t1, t2) 

l = [] 
for _ in range(50): 
    l.append(randint(0,1000)) 
t1 = min(repeat(stmt='lin_get_combos(l)', setup='from __main__ import lin_get_combos, l', repeat=10, number=100)) 
t2 = min(repeat(stmt='craig_get_combos(l)', setup='from __main__ import craig_get_combos, l', repeat= 10, number=100)) 
print '%0.5f, %0.5f' % (t1, t2) 

l = [] 
for _ in range(500): 
    l.append(randint(0,1000)) 
t1 = min(repeat(stmt='lin_get_combos(l)', setup='from __main__ import lin_get_combos, l', repeat=10, number=10)) 
t2 = min(repeat(stmt='craig_get_combos(l)', setup='from __main__ import craig_get_combos, l', repeat= 10, number=10)) 
print '%0.5f, %0.5f' % (t1, t2) 

l = [] 
for _ in range(1000): 
    l.append(randint(0,1000)) 
t1 = min(repeat(stmt='lin_get_combos(l)', setup='from __main__ import lin_get_combos, l', repeat=3, number=3)) 
t2 = min(repeat(stmt='craig_get_combos(l)', setup='from __main__ import craig_get_combos, l', repeat= 3, number=3)) 
print '%0.5f, %0.5f' % (t1, t2) 

l = [] 
for _ in range(5000): 
    l.append(randint(0,1000)) 
t1 = min(repeat(stmt='lin_get_combos(l)', setup='from __main__ import lin_get_combos, l', repeat=3, number=1)) 
t2 = min(repeat(stmt='craig_get_combos(l)', setup='from __main__ import craig_get_combos, l', repeat= 3, number=1)) 
print '%0.5f, %0.5f' % (t1, t2) 
+0

ありがとうクレイグ、あなたのコードはかなりクールです。並べ替えることによってさらに改善する方法があると思いますか? –

+0

ところで、私はあなたのコードがより速い理由を考えています。なぜなら、itertools.combinationsが2つのネストされたループを書くよりも速いからです。 –

+1

@LinMaはい、唯一の違いは、itertools.combinationsを使用してユニークなインデックスペアのセットを取得するのに対し、OPではネストされたforループを使用することです。一般的には、タイミング結果が示すように、独自のPythonコードを 'do x 'に書くのではなく、' do x'にPythonライブラリを使用する方が良いでしょう。並べ替えが行われる限り、私はいくつかのエッジケースを除いて、大幅な速度改善を示すソートを利用できるテクニックはないと推測します。その理由は、特定の値よりも小さいか大きい値だけでなく、すべての合計を調べる必要があるからです。 –

関連する問題