単純な辞書にデータを保存すると、これをさらに最適化することはできません。何も予測できない順序で辞書のすべての要素への順次アクセスを提供しないためです。これは、あなたの解がO(n)
より速くないことを意味します。
今、データベース。データベースは、(複雑な)問題の普遍的な解決策ではありません。このようなデータベースのルックアップの速度/複雑さを確実に見積もることはできますか?この返信の最後までスクロールすると、大きなデータセットの場合、データベースのパフォーマンスがスマートなデータ構造よりもはるかに悪い可能性があります。
ここで必要なものは手作りのデータ構造です。多くの選択肢がありますが、このデータを使って他のものに強く依存しています。たとえば、N
のキーの並べ替えられたリストを、それぞれn
番目のタプル要素でソートしておくことができます。次に、位置n
にある1つのタプル要素にのみ一致する要素の並べ替えられたセットのN
を素早く選択し、それらの交差を見つけて結果を得ることができます。これにより、平均パフォーマンスがO(log n)*O(m)
になります。ここで、mは1つのサブセットの平均要素数です。
あなたはk-dツリーにアイテムを保存することができます。つまり、挿入価格はO(log n)
でなければなりませんが、上記のようなクエリをO(log n)
時間行うことができます。ここでscipyのダウンロードからkd木の実装を使用して、Pythonでの例です:
from scipy.spatial import kdtree
import itertools
import random
random.seed(1)
data = list(itertools.permutations(range(10), 4))
random.shuffle(data)
data = data[:(len(data)/2)]
tree = kdtree.KDTree(data)
def match(a, b):
assert len(a) == len(b)
for i, v in enumerate(a):
if v != b[i] and (v is not None) and (b[i] is not None):
return False
return True
def find_like(kdtree, needle):
assert len(needle) == kdtree.m
def do_find(tree, needle):
if hasattr(tree, 'idx'):
return list(itertools.ifilter(lambda x: match(needle, x),
kdtree.data[tree.idx]))
if needle[tree.split_dim] is None:
return do_find(tree.less, needle) + do_find(tree.greater, needle)
if needle[tree.split_dim] <= tree.split:
return do_find(tree.less, needle)
else:
return do_find(tree.greater, needle)
return do_find(kdtree.tree, needle)
def find_like_bf(kdtree, needle):
assert len(needle) == kdtree.m
return list(itertools.ifilter(lambda x: match(needle, x),
kdtree.data))
import timeit
print "k-d tree:"
print "%.2f sec" % timeit.timeit("find_like(tree, (1, None, 2, None))",
"from __main__ import find_like, tree",
number=1000)
print "brute force:"
print "%.2f sec" % timeit.timeit("find_like_bf(tree, (1, None, 2, None))",
"from __main__ import find_like_bf, tree",
number=1000)
そして、テスト実行結果:
$ python lookup.py
k-d tree:
0.89 sec
brute force:
6.92 sec
楽しみのためだけに、また、データベースベースのソリューションのベンチマークを追加しました。ベンチマークごと(キーのセット657720要素を結果として生じるため)
import sqlite3
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE a (x1 INTEGER, x2 INTEGER, x3 INTEGER, x4 INTEGER)")
db.execute("CREATE INDEX x1 ON a(x1)")
db.execute("CREATE INDEX x2 ON a(x2)")
db.execute("CREATE INDEX x3 ON a(x3)")
db.execute("CREATE INDEX x4 ON a(x4)")
db.executemany("INSERT INTO a VALUES (?, ?, ?, ?)",
[[int(x) for x in value] for value in tree.data])
def db_test():
cur = db.cursor()
cur.execute("SELECT * FROM a WHERE x1=? AND x3=?", (1, 2))
return cur.fetchall()
print "sqlite db:"
print "%.2f sec" % timeit.timeit("db_test()",
"from __main__ import db_test",
number=100)
と試験結果、100回の実行のために低下:今
random.seed(1)
data = list(itertools.permutations(range(30), 4))
random.shuffle(data)
、「データベース」実装:初期化コードは、上からに変更しました:
$ python lookup.py
building tree
done in 6.97 sec
building db
done in 11.59 sec
k-d tree:
1.90 sec
sqlite db:
2.31 sec
このビルドツリーでは、このテストデータセットをデータベースに挿入するのに要する時間がほぼ2倍短縮されました。ここ
完全なソース:https://gist.github.com/1261449
'None'sはkeyWords''内の任意の位置に現れることはできますか? – NPE
+1は答えに 'reduce'がどこにあるか質問します。 – SingleNegationElimination
はい、任意の数の任意の位置に任意の数を指定できます。 – combatdave