を使用できます。bk-tree
各アイテムを追加する前に、他のアイテムから距離D以内にないことを確認してください(このアイデアのコメントにある@DietrichEppに感謝します。
このレシピはbk-treeに使用できます(ただし、同様のレシピは簡単に変更できます)。単に2つの変更を加えます:行を変更します:
def __init__(self, items, distance, usegc=False):
に
def __init__(self, items, distance, threshold=0, usegc=False):
そして、行を変更します
if el not in self.nodes: # do not add duplicates
に
if (el not in self.nodes and
(threshold == None or len(self.find(el, threshold)) == 0)):
これにより、アイテムが追加されたときに重複がないことが保証されます。次に、リストから重複を削除するコードは次のとおりです。
from Levenshtein import distance
from bktree import BKtree
def remove_duplicates(lst, threshold):
tr = BKtree(iter(lst), distance, threshold)
return tr.nodes.keys()
これは、距離関数をpython-Levenshteinパッケージに依存していることに注意してください。これは、bk-treeによって提供されるものよりもはるかに高速です。python-LevenshteinにはCでコンパイルされたコンポーネントがありますが、インストールする価値があります。
最後に、単語数を増やして(からランダム/usr/share/dict/words
に取得)パフォーマンステストを設定し、それぞれの実行にかかった時間をグラフ化しました。
import random
import time
from Levenshtein import distance
from bktree import BKtree
with open("/usr/share/dict/words") as inf:
word_list = [l[:-1] for l in inf]
def remove_duplicates(lst, threshold):
tr = BKtree(iter(lst), distance, threshold)
return tr.nodes.keys()
def time_remove_duplicates(n, threshold):
"""Test using n words"""
nwords = random.sample(word_list, n)
t = time.time()
newlst = remove_duplicates(nwords, threshold)
return len(newlst), time.time() - t
ns = range(1000, 16000, 2000)
results = [time_remove_duplicates(n, 3) for n in ns]
lengths, timings = zip(*results)
from matplotlib import pyplot as plt
plt.plot(ns, timings)
plt.xlabel("Number of strings")
plt.ylabel("Time (s)")
plt.savefig("number_vs_time.pdf")
数学的に確認しないと、2次式ではないと思います。実際にn log n
は、bkツリーへの挿入がログ時間演算である場合は理にかなっていると思います。最も注目すべきは、5000未満の文字列で非常に高速に実行されることです。これは、OPの目標であることが期待されます(従来のforループソリューションでは不可能だった15000では妥当です)。