web-dev-qa-db-ja.com

Python dictへのアクセスの時間の複雑さ

簡単なPythonプログラムを書いています。

私のプログラムは、辞書への線形アクセスに悩まされているようです。アルゴリズムが2次であっても、ランタイムは指数関数的に増加します。
辞書を使用して値をメモします。それがボトルネックのようです。

私がハッシュしている値は、ポイントのタプルです。各ポイントは:(x、y)、0 <= x、y <= 50
ディクショナリの各キーは次のとおりです:2-5ポイントのタプル:((x1、y1)、(x2、y2)、(x3、y3)、(x4、y4))

キーは、書き込まれるよりも何倍も頻繁に読み取られます。

python dictsがそのような入力での線形アクセス時間の影響を受けることは正しいですか?

私の知る限り、セットは対数アクセス時間を保証しています。
Pythonでセット(または同様のもの)を使用してディクテーションをシミュレートするにはどうすればよいですか?

編集リクエストに従って、メモ化関数の(簡略化された)バージョンを以下に示します。

def memoize(fun):
    memoized = {}
    def memo(*args):
        key = args
        if not key in memoized:
            memoized[key] = fun(*args)
        return memoized[key]
    return memo
36
x10

Time Complexity を参照してください。 python dictはハッシュマップです。したがって、最悪のケースはO(n)で、ハッシュ関数が不適切で、多くの衝突が発生する場合です。ただし、追加されたすべてのアイテムが同じハッシュを持ち、同じチェーンに追加されるという非常にまれなケースです。これはメジャーPython実装の場合、極端にありそうもない平均的な時間の複雑さはもちろんO(1)です。

最良の方法は、使用しているオブジェクトのハッシュをチェックして確認することです。 CPython Dictint PyObject_Hash(PyObject * o) を使用します。これはhash(o)と同等です。

簡単なチェックの後、まだ同じ値にハッシュする2つのタプルを見つけることができていません。これは、ルックアップがO(1)であることを示します。

l = []
for x in range(0, 50):
    for y in range(0, 50):
        if hash((x,y)) in l:
            print "Fail: ", (x,y)
        l.append(hash((x,y)))
print "Test Finished"

CodePad (24時間利用可能)

58
Yacoby

あなたは正しくありません。 dictアクセスは、ここでは問題になりそうにありません。非常に奇妙な入力や非常に悪いハッシュ関数がない限り、ほぼ確実にO(1)です。より良い診断のために、アプリケーションからいくつかのサンプルコードを貼り付けます。

4
Eli Bendersky

サンプルのコードとデータを提供すると、提案を行うのが簡単になります。

辞書へのアクセスは、その操作が平均して O(1)であり、O(N) amortized worst case であるため、問題になる可能性は低いです。のハッシュ関数でデータの衝突が発生しています。組み込みのハッシュ関数で問題が発生している場合は、独自のハッシュ関数を指定できます。

Pythonのディクショナリ実装は、キーオブジェクトが「ハッシュ」関数を提供することを要求することにより、ディクショナリルックアップの平均的な複雑さをO(1)に減らします。このようなハッシュ関数は、キーオブジェクトの情報を取得して使用しますハッシュ値と呼ばれる整数を生成します。このハッシュ値は、この(キー、値)ペアをどの「バケット」に入れるかを決定するために使用されます。

クラスの__hash__メソッドを上書きして、次のようなカスタムハッシュ関数を実装できます。

def __hash__(self):    
    return hash(str(self))

データが実際にどのように見えるかに応じて、標準関数よりも衝突の少ない高速なハッシュ関数を考え出すことができる場合があります。ただし、これはありそうもないことです。詳細については、 辞書キーのPython Wikiページ を参照してください。

3
James Thompson

特定の質問に答えるには:

Q1: "" "私は正しいのですpython dictsはこのような入力での線形アクセス時間の影響を受けますか?" ""

A1:平均ルックアップ時間がO(N)である場合、Nは辞書のエントリ数です)は、あなたが間違っている可能性が高いです。正しい場合、 Pythonコミュニティは、どのような状況であなたが正しいかを知りたいので、問題を軽減または少なくとも警告することができます。「サンプル」コードも「簡略化された」コードも役に立ちません。問題を再現する実際のコードとデータを示してください。コードには、各Pのディクテーションアイテムの数やディクショナアクセスの数などを含める必要があります。ここで、Pはキーのポイント数です(2 <= P <= 5)

Q2: "" "私が知る限り、セットは対数アクセス時間を保証しています。Pythonでセット(または類似のもの)を使用してディクテーションをシミュレートするにはどうすればよいですか?" ""

A2:セットは、どのコンテキストで対数アクセス時間を保証していますか? Python実装についてはそのような保証はありません。最近のCPythonバージョンは、実際にはカットダウンdict実装(キーのみ、値なし)を使用しているため、平均はO(1)動作。どのような言語でも、セットや類似のディクテーションをどのようにシミュレートできますか?短い答え:dict.has_key(key)以外の機能が必要な場合、非常に困難です。

3
John Machin

私のプログラムは、辞書への線形アクセスに悩まされているようです。アルゴリズムが2次であっても、ランタイムは指数関数的に増加します。

辞書を使用して値をメモします。それがボトルネックのようです。

これは、メモ化メソッドのバグの証拠です。

2
Robert Rossney

他の人が指摘しているように、Pythonでdictsにアクセスするのは高速です。それらは中心的な役割を考えると、おそらく言語の中で最も油を塗ったデータ構造です。問題は他の場所にあります。

いくつのタプルを覚えていますか?メモリのフットプリントを考慮しましたか?おそらく、メモリアロケータまたはページングメモリにすべての時間を費やしているのでしょう。

1
Ned Batchelder