TLD
Sử dụng phương pháp này (với thiết lập tra cứu) nếu bạn muốn giải pháp nhanh nhất. Đối với một tập dữ liệu tương tự như của OP, nó nhanh hơn khoảng 2000 lần so với câu trả lời được chấp nhận.
Nếu bạn khăng khăng sử dụng regex để tra cứu, hãy sử dụng phiên bản dựa trên bộ ba này , tốc độ này vẫn nhanh hơn 1000 lần so với liên minh regex.
Học thuyết
Nếu câu của bạn không phải là chuỗi hài hước, có thể xử lý nhiều hơn 50 mỗi giây.
Nếu bạn lưu tất cả các từ bị cấm vào một bộ, sẽ rất nhanh để kiểm tra xem một từ khác có được bao gồm trong bộ đó không.
Đóng gói logic vào một hàm, cung cấp hàm này làm đối số re.sub
và bạn đã hoàn tất!
Mã
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)
Các câu đã chuyển đổi là:
' . !
.
GiraffeElephantBoat
sfgsdg sdwerha aswertwe
Lưu ý rằng:
- tìm kiếm không phân biệt chữ hoa chữ thường (nhờ
lower()
)
- thay thế một từ bằng
""
có thể để lại hai khoảng trắng (như trong mã của bạn)
- Với python3,
\w+
cũng khớp các ký tự có dấu (ví dụ "ångström"
).
- Bất kỳ ký tự không phải từ nào (tab, dấu cách, dòng mới, dấu, ...) sẽ không bị ảnh hưởng.
Hiệu suất
Có một triệu câu, banned_words
có gần 100000 từ và kịch bản chạy trong chưa đầy 7 giây.
Để so sánh, câu trả lời của Liteye cần 160 giây cho 10 nghìn câu.
Với n
tổng số lượng từ và m
số lượng từ bị cấm, mã của OP và Liteye là O(n*m)
.
Trong so sánh, mã của tôi nên chạy trong O(n+m)
. Xem xét rằng có nhiều câu hơn các từ bị cấm, thuật toán trở thành O(n)
.
Kiểm tra công đoàn Regex
Sự phức tạp của tìm kiếm regex với một '\b(word1|word2|...|wordN)\b'
mẫu là gì? Là nó O(N)
hay O(1)
?
Thật khó để nắm bắt cách thức hoạt động của công cụ regex, vì vậy hãy viết một bài kiểm tra đơn giản.
Mã này trích xuất 10**i
các từ tiếng Anh ngẫu nhiên vào một danh sách. Nó tạo ra liên kết regex tương ứng và kiểm tra nó bằng các từ khác nhau:
- một rõ ràng không phải là một từ (nó bắt đầu bằng
#
)
- một là từ đầu tiên trong danh sách
- một là từ cuối cùng trong danh sách
- một cái trông giống như một từ nhưng không
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))
Nó xuất ra:
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
Vì vậy, có vẻ như việc tìm kiếm một từ duy nhất với một '\b(word1|word2|...|wordN)\b'
mẫu có:
O(1)
trường hợp tốt nhất
O(n/2)
trường hợp trung bình, vẫn còn O(n)
O(n)
trường hợp xấu nhất
Những kết quả này phù hợp với một tìm kiếm vòng lặp đơn giản.
Một sự thay thế nhanh hơn nhiều cho một liên minh regex là tạo ra mô hình regex từ một trie .