Я подумал, что поделюсь этим немного интересным трюком, хотя он занимает немного больше кода, чем остальные, и на самом деле не "питонический".Это займет немного больше кода, чем другие решения, но должно быть достаточно быстрым, если я посмотрю на время, необходимое другим.
Мы делаем небольшую предварительную обработку для ускорения вычислений.Основной подход заключается в следующем: мы присваиваем каждой букве алфавита простое число.Например, A = 2, B = 3 и так далее.Затем мы вычисляем хеш для каждого слова в алфавите, который является просто произведением простых представлений каждого символа в слове.Затем мы сохраняем каждое слово в словаре, индексируемом хэшем.
Теперь, если мы хотим выяснить, какие слова эквивалентны слову textbook
, нам нужно только вычислить тот же хеш для слова и найти егов нашем словаре.Обычно (скажем, в C ++) нам приходится беспокоиться о переполнениях, но в python это даже проще: каждое слово в списке с таким же индексом будет содержать точно такие же символы.
Вот код снебольшая оптимизация, что в нашем случае нам нужно беспокоиться только о символах, которые также встречаются в данном слове, что означает, что мы можем обойтись гораздо меньшей таблицей простых чисел, чем в противном случае (очевидная оптимизация будет заключаться в назначении только тех символов, которые появляются в словезначение вообще - это было достаточно быстро, так что я не стал беспокоиться, и таким образом мы могли предварительно обработать только один раз и сделать это для нескольких слов).Первичный алгоритм достаточно полезен, так что вы все равно должны его использовать;)
from collections import defaultdict
from itertools import permutations
PRIMES = list(gen_primes(256)) # some arbitrary prime generator
def get_dict(path):
res = defaultdict(list)
with open(path, "r") as file:
for line in file.readlines():
word = line.strip().upper()
hash = compute_hash(word)
res[hash].append(word)
return res
def compute_hash(word):
hash = 1
for char in word:
try:
hash *= PRIMES[ord(char) - ord(' ')]
except IndexError:
# contains some character out of range - always 0 for our purposes
return 0
return hash
def get_result(path, given_word):
words = get_dict(path)
given_word = given_word.upper()
result = set()
powerset = lambda x: powerset(x[1:]) + [x[:1] + y for y in powerset(x[1:])] if x else [x]
for word in (word for word in powerset(given_word) if len(word) >= 4):
hash = compute_hash(word)
for equiv in words[hash]:
result.add(equiv)
return result
if __name__ == '__main__':
path = "dict.txt"
given_word = "textbook"
result = get_result(path, given_word)
print(result)
Запускается в моем списке слов Ubuntu (98k слов) довольно быстро, но не то, что я бы назвал Pythonic, так как это в основномпорт алгоритма c ++.Полезно, если вы хотите сравнить более одного слова таким образом ..