web-dev-qa-db-ja.com

このコードは文字列の近くを見つけることができますか?

入力文字列を考えると、私は他のすべての範囲内にある文字列 レーベンシュタイン距離 2.例えば、入力文字列が「AAA」で、アルファベットが[ある場合「」のセットを見つけるしたいと思い、 「B」]その後、我々は持っています:

{ 'baaa'、 'AAB'、 ''、 'AAAAA'、 'BAAB'、 'abbaa'、 'ABAA'、 'aaabb'、 'ABB'、 'AAAB'、 'アベバ'、 'A'、 ' AABB」、 'ババ'、 'baaab'、 'aabab'、 'aaaab'、 'abaaa'、 'aabaa'、 'bbaaa'、 'abaab'、 'AAAA'、 'baaaa'、 'BAB'、 'BBA' 、 'ABA'、 'aaaba'、 'BA'、 'aabba'、 'ABAB'、 'BAA'、 'AAA'、 'bbaa'、 'baaba'、 'AABA'、 'アバ'、 'AB'、 ' babaa '}

私はこれを行うには、コードを持っているが、それは非効率的です。ここでは、アルファベットと入力文字列aaaaaaaaaaなど、すべての印刷可能なASCII文字を使用しています。

import string

input_string = "a" * 10
f = (
    lambda input_string, dist=2, i=0: dist * input_string[i - 1 :]
    and {
        k[:i] + char + k[i + w:]
        for k in f(input_string, dist - 1)
        for char in [""] + list(string.printable)
        for w in (0, 1)
    }
    | f(input_string, dist, i + 1)
    or {input_string}
)
f(input_string)
 _

私は別の入力文字列でこれを何度も実行する必要があります。このコードは1631129個の異なる文字列を作るために私のデスクトップ上に現在2.9sかかります。誰もが非常に速く、それを作る方法を見ることができますか?


リーグテーブルこれまでに(使用して印刷可能なアルファベットなど):

私のコード:2.98秒±146ミリ

アランT.のコード:1.58秒±60.7ミリ。現在の勝者。

dDGのコード:1.85秒±28.4ミリ

6
fomin

可能な編集の全セットを生成することができる大きなスピードの改善を得ることができるとは思わない。

最初の編集後に同じ結果を生み出す文字列の再拡張を回避することで、35%から実行時間の50%を剃ることができます。これは、より高い編集距離とより大きな違いを与えるでしょう。改善レベルは、実際の文字列/単語(単一の繰り返し文字で構成されていない可能性がある)によっても依存します。

いずれにせよ、これが最適化された機能の機能です。

import string
from collections import deque

def makeEdits(S,count=1,charSet=None):
    if charSet is None: charSet = string.printable
    result   = set([S])
    expand   = deque(result)
    lastEdit = False

    def addEdit(s):
        if s in result: return 
        result.add(s)
        if not lastEdit: expand.append(s)

    for edit in range(count):
        editing = expand.copy()
        expand.clear()
        lastEdit = edit == count-1
        for eString in editing:
            for i,char in enumerate(eString):
                left,right = eString[:i],eString[i+1:]                       
                addEdit(left+right)                  # deletion
                for newChar in charSet:                    
                    addEdit(left+newChar+char+right) # insertions before
                    addEdit(left+newChar+right)      # replacement                   
            for newChar in charSet:
                addEdit(eString+newChar)             # insertion at end
    return result
 _

ラップトップ上のパフォーマンステスト:

from timeit import timeit
count = 1
input_string = "a" * 10

print('makeEdits()...')
print(len(makeEdits(input_string,2)))
t = timeit(lambda:makeEdits(input_string,2),number=count)
print(t)

print('f()...')
print(len(f(input_string)))
t = timeit(lambda:f(input_string),number=count)
print(t)
 _

...

makeEdits()...
1631129
2.0302121050000004
f()...
1631129
3.145120027999999
 _

String.ascii_letpersなどの文字セットが小さいほど、(より少ない文字列を生成することによって)パフォーマンス結果がかなり改善されます。

timeit(lambda:makeEdits(input_string,2,string.ascii_letters),number=count)

# 0.48669491199999015

len(makeEdits(input_string,2,string.ascii_letters)) # 433913

timeit(lambda:makeEdits(input_string,2,string.ascii_lowercase),number=count)

# 0.10699477299999671

len(makeEdits(input_string,2,string.ascii_lowercase)) # 104805
 _

スペルチェッカーを作っている場合は、処理前にすべてを小文字に変換し、文字セットに含める単一の文字としてすべての特殊文字を扱うことができます。これにより、プログラムは、より小さな文字セットのパフォーマンスブーストを取得できるようになります(この場合は30倍高速)。文字列の前処理を加え、その後の結果の調整をいくつか追加する必要があります。

3
Alain T.