TLDR
가장 빠른 솔루션을 원하면이 방법 (조회 설정 사용)을 사용하십시오. OP와 유사한 데이터 세트의 경우 허용되는 답변보다 약 2000 배 빠릅니다.
조회에 정규식을 사용해야한다고 주장하는 경우이 트라이 기반 버전을 사용하십시오. 이 버전 은 정규식 조합보다 여전히 1000 배 빠릅니다.
이론
문장이 엄청 나지 않은 문자열이라면 초당 50 개 이상을 처리하는 것이 가능할 것입니다.
모든 금지 된 단어를 세트에 저장하면 해당 세트에 다른 단어가 포함되어 있는지 확인하는 것이 매우 빠릅니다.
논리를 함수로 묶고이 함수를 인수로 제공하면 re.sub
완료됩니다!
암호
import re
with open('/usr/share/dict/american-english') as wordbook:
banned_words = set(word.strip().lower() for word in wordbook)
def delete_banned_words(matchobj):
word = matchobj.group(0)
if word.lower() in banned_words:
return ""
else:
return word
sentences = ["I'm eric. Welcome here!", "Another boring sentence.",
"GiraffeElephantBoat", "sfgsdg sdwerha aswertwe"] * 250000
word_pattern = re.compile('\w+')
for sentence in sentences:
sentence = word_pattern.sub(delete_banned_words, sentence)
변환 된 문장은 다음과 같습니다.
' . !
.
GiraffeElephantBoat
sfgsdg sdwerha aswertwe
참고 :
- 검색은 대소 문자를 구분하지 않습니다 (감사합니다
lower()
).
- 단어를 바꾸면
""
공백이 남을 수 있습니다 (코드에서와 같이)
- python3을 사용하면
\w+
악센트 부호가있는 문자 (예 :) 와도 일치 "ångström"
합니다.
- 단어가 아닌 문자 (탭, 공백, 줄 바꿈, 표시 등)는 그대로 유지됩니다.
공연
백만 개의 문장이 banned_words
있고 거의 10 만 단어가 있으며 스크립트는 7 초 이내에 실행됩니다.
이에 비해 Liteye의 답변 에는 1 만 개의 문장에 160이 필요했습니다.
함께 n
단어의 총 amound하고있는 m
금지 된 단어의 양, 영업 이익의 및 Liteye의 코드입니다 O(n*m)
.
이에 비해 내 코드는에서 실행되어야합니다 O(n+m)
. 금지 된 단어보다 더 많은 문장이 있다는 것을 고려하면 알고리즘이됩니다 O(n)
.
정규식 조합 테스트
'\b(word1|word2|...|wordN)\b'
패턴 을 사용한 정규식 검색의 복잡성은 무엇입니까 ? 그것은인가 O(N)
또는 O(1)
?
정규식 엔진이 작동하는 방식을 파악하기는 어렵 기 때문에 간단한 테스트를 작성해 보겠습니다.
이 코드는 10**i
임의의 영어 단어를 목록으로 추출 합니다. 해당 정규 표현식 조합을 만들고 다른 단어로 테스트합니다.
- 하나는 분명히 단어가 아닙니다 (로 시작합니다
#
)
- 하나는 목록에서 첫 번째 단어입니다
- 하나는 목록의 마지막 단어입니다
- 하나는 단어처럼 보이지만 그렇지 않습니다
import re
import timeit
import random
with open('/usr/share/dict/american-english') as wordbook:
english_words = [word.strip().lower() for word in wordbook]
random.shuffle(english_words)
print("First 10 words :")
print(english_words[:10])
test_words = [
("Surely not a word", "#surely_NöTäWORD_so_regex_engine_can_return_fast"),
("First word", english_words[0]),
("Last word", english_words[-1]),
("Almost a word", "couldbeaword")
]
def find(word):
def fun():
return union.match(word)
return fun
for exp in range(1, 6):
print("\nUnion of %d words" % 10**exp)
union = re.compile(r"\b(%s)\b" % '|'.join(english_words[:10**exp]))
for description, test_word in test_words:
time = timeit.timeit(find(test_word), number=1000) * 1000
print(" %-17s : %.1fms" % (description, time))
출력합니다 :
First 10 words :
["geritol's", "sunstroke's", 'fib', 'fergus', 'charms', 'canning', 'supervisor', 'fallaciously', "heritage's", 'pastime']
Union of 10 words
Surely not a word : 0.7ms
First word : 0.8ms
Last word : 0.7ms
Almost a word : 0.7ms
Union of 100 words
Surely not a word : 0.7ms
First word : 1.1ms
Last word : 1.2ms
Almost a word : 1.2ms
Union of 1000 words
Surely not a word : 0.7ms
First word : 0.8ms
Last word : 9.6ms
Almost a word : 10.1ms
Union of 10000 words
Surely not a word : 1.4ms
First word : 1.8ms
Last word : 96.3ms
Almost a word : 116.6ms
Union of 100000 words
Surely not a word : 0.7ms
First word : 0.8ms
Last word : 1227.1ms
Almost a word : 1404.1ms
따라서 '\b(word1|word2|...|wordN)\b'
패턴이 있는 단일 단어를 검색하는 것처럼 보입니다 .
O(1)
가장 좋은 경우
O(n/2)
여전히 평균 사례 O(n)
O(n)
최악의 경우
이 결과는 간단한 루프 검색과 일치합니다.
정규식 조합에 대한 훨씬 빠른 대안 은 트라이에서 정규식 패턴 을 만드는 것 입니다.