2016-04-30 4 views
3

私は次のPythonコードを実行しようとしています。これはアルファベット順に "ABCDEGGHIJK"の最初の置換を返します。これはProject Eulerの問題336で定義されているような非常に単純なソートアルゴリズムです。ここで正確なpythonコードは、高速のマシンでは20倍遅くなりますか?

は、コード(悪い変数名の謝罪)です:

from itertools import permutations 

def first_out_letter(st): 
    """ 
    returns the first letter alphabetically in st which is not in sorted order 
    alphabetically, string must be all in captials. 
    """ 
    def first(string): 
     """ 
     returns the first alphabetical letter in a string, only capitals allowed 
     """ 
     alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 

     for i in alpha: 
      if i in string: 
       return i 
     return None 
    sor = ''.join(sorted(st)) 
    for i in range(len(st)): 
     if st[i] != sor[i]: 
      return first(st[i:]) 
    return None 

def get_arrangement_size(arrang,dictionary): 
    """ 
    returns the number of shifts needed to arrange a string in lexographic order 
    using a dumb method of first getting the first digit correct, then the second 
    and so on... 

    the argument dictionary stores precomputed results and is modified during execution. 
    """ 
    if arrang in dictionary.keys(): 
     return dictionary[arrang] 
    sor = ''.join(sorted(arrang)) 
    if arrang == sor: 
     dictionary[arrang] = 0 
     return 0 
    else: 
     bing = first_out_letter(arrang) 
     num_arr = 0 
     pos_bing = 0 
     for i in range(len(arrang)): 
      if arrang[i] == sor[i]: 
       num_arr += 1 
      else: 
       break 
     for i in range(len(arrang)): 
      if arrang[i] != bing: 
       pos_bing += 1 
      else: 
       break 
     if bing == arrang[-1]: 
      low = get_arrangement_size(arrang[:num_arr]+arrang[num_arr:][::-1],dictionary) 
     else: 
      low = get_arrangement_size(arrang[:pos_bing]+arrang[pos_bing:][::-1],dictionary) 
     dictionary[arrang] = low+1    
     return low+1 

solutions = {} 
letters = ["A","B","C","D","E","F","G","H","I","J","K"] 
piv = permutations(letters) 
for item in piv: 
    get_arrangement_size(''.join(item),solutions) #builds up the solutions dictionary 
ma = max(solutions.values()) 
fir = [] 
for item in solutions.keys(): 
    if solutions[item] == ma: 
     fir.append(item) 
fir = sorted(fir) 
print(fir[0]) 

コードは、両方の私のマシン上で正常に動作し、正しい答えを与えるが、私は最大20の非常に大きな速度差を見ています私の2台のマシンで

私の(理論的には)より速いi5搭載のコンピュータはLinux Mintとpython 2.7.6を実行していますが、このコードを実行すると、Celeronの低速コンピュータよりもはるかに遅く実行されますWindowsとpythonを使って3.5.1。私は私のマシンのどちらかでこのコードを実行すると、同じIDE(Spyder)を使用しているので、この速度差がある理由はわかりません。

これを説明する助けまたは理由は非常に高く評価されます。

編集:Chrissの提案によれば、このコードをPython 2.7で低速コンピュータで実行しようとしましたが、同じコンピュータ上で3.5でコードを実行したときよりもはるかに低速でした。だから、この違いは、Pythonのバージョンによって引き起こされますが、正確に何が私は知らないし、まだ知りたいと思う違いを引き起こすものです。

+0

あなたは、Linux上でWindows上のPython 2.7、またはPythonの3.5を試したことがありますか? – Chris

+0

@Chris。私はちょうどやったし、Windows上のPython 2.7もPython 3.5よりもはるかに遅かったので、この違いはPythonのバージョンのためだと思います。しかし、この違いを引き起こすのはまさに私はまだ分かりませんし、見つけたいと思います。 –

+0

これは、Python 2とPython 3の 'range 'の違いが原因である可能性があります。すべての' range'呼び出しを 'xrange'に変換し、それがPython 2上で実行して助けてくれたら教えてください。私は自分でやるが、元のコードは私のマシンでメモリエラーになる... – niemmi

答えて

4

これは、Python 2と3の間でdict.keys()の違いが原因です。Python 2では、dict.keys()はリストとしてキーのコピーを作成して返します。 Python 3 dict.keys()ではdictionary viewが返され、代わりにsetというオブジェクトがあります。 listから要素が見つかるかどうかを調べるのは、その違いを説明しているsetにあるかどうかを確認するよりもはるかに遅いです。

次のコードは、Python 2 & 3に同じ時間におよそ走る変更作る場合:

if arrang in dictionary: # Instead of if arrang in dictionary.keys() 
    return dictionary[arrang] 
+0

ありがとう、これは非常にうまくいきます。しかし、ある要素が辞書の値を持っているかどうかをチェックしたいのであれば、もし私が間違っていなければ 'set in dictionary(dictionary.values()):if要素を使う必要があります。これは正しいです? –

+1

@AbdulHadiKhan:はい、それを使用することはできますが、dictionary.values()の要素だけではなくなります。 'set'を生成し、valueが' list'の中にあるかどうかを調べることは、 'O(n)'時間の複雑さを持ちます。キーと値の両方をすばやく確認する必要がある場合は、値を['Counter'](https://docs.python.org/2/library/collections.html#collections.Counter)のように別々のコンテナに格納する方がよいでしょう。 (値が一意であることが分かっていれば 'set'も同様に行います)。 – niemmi

関連する問題