Я пытаюсь сделать игру по телешоу Cifras y lettras в Испании (Countdown в Великобритании или Des Chiffres et des lettres во Франции). У меня есть список всех испанских слов благодаря найденному мной коду, который позволил мне парсить официальный сайт. При этом я решил составить словарь со всеми буквами и их распространенностью, чтобы затем сгенерировать случайную (взвешенную) строку из 10 символов, из которой игрок попытается составить максимально длинное слово. Проблема возникает, когда я дохожу до вычисления самого длинного слова из этой 10-буквенной строки.
Здесь буквы (random_string при звонке) — это сгенерированная мной строка из 10 символов, а word_list (filtered_word) — список всех слов на испанском языке, содержащих 10 букв или меньше.
def find_longest_word(letters, word_list):
# Sort word_list by length (longest to shortest)
word_list.sort(key=len, reverse=True)
# Convert word_list to a set for fast lookup
word_set = set(word_list)
longest_word = ''
max_length = 0
# Iterate over lengths from len(letters) down to 1
for length in range(len(letters), 0, -1):
# Filter word_list to include only words of current length
filtered_words = [word for word in word_list if len(word) == length]
# Generate permutations of current length
for perm in permutations(letters, length): #permutations() given letter set and length gives all permutations
# The outer for loop will make it so that we start by 10 letter words and after obtaining all permutations of that length we go to 9
perm_word = ''.join(perm)
if perm_word in filtered_words:
if length > max_length:
max_length = length
longest_word = perm_word
# Exit the loop once the longest word is found
return longest_word
return longest_word
longest_word = find_longest_word(random_string, filtered_words)
Это выполняется 1 час и все еще продолжается. Я понимаю, что способов, которыми я занимаюсь, всего 10! (3,6 млн) перестановок, которые нужно проверить на 81 000 слов на испанском языке (хотя и меньше, поскольку я сравниваю строку из 10 букв только со словами одинаковой длины (я не уверен, что это что-то меняет, поскольку ее все равно нужно анализировать) через каждое слово, чтобы проверить его длину)).
https://github.com/Albertofma/Letras — это весь проект на случай, если кто-то захочет увидеть остальную часть кода.






Пара вещей с точки зрения алгоритма может вам помочь....
Если вы придерживаетесь имеющегося у вас кода, вам следует создать filtered_words a set. Вы выполняете 3,6 млн поисков в списке с 80 тыс. элементов, это будет медленно. А если это не поможет, вам придется выполнить еще 3M плюс поиск P(10,9) и т. д.
Подумайте о том, чтобы заранее «поработать» над словами (как показано ниже). Если вы отсортируете буквы в каждом из примерно 80 тыс. слов, которые можно проверить (что очень быстро для небольших списков), то вы сможете избежать перестановки образцов букв, и у вас останется только пара сотен (максимум) сравнений, потому что вы можете используйте комбинации букв, отсортированные. И sum(C(10,10)=1 , C(10,9) = 10 , C(10, 8) = 45, ... ) КРОШЕЧНАЯ по сравнению с суммой перестановок.
Этот мод вашего кода выполняется менее чем за секунду:
import random #To make the random string given to the player
from collections import Counter, defaultdict
from itertools import permutations, \
combinations # For obtaining all permutations for a 10 letter set
# Define the file path
file_path = 'palabras.txt'
#Dictionary with accented letters and their unaccented counterparts to be replaced by
accented_to_unaccented = {
'é': 'e', 'í': 'i', 'ú': 'u', 'á': 'a', 'ó': 'o', 'ü': 'u', 'è': 'e'
}
# Initialize an empty list to store the filtered words
filtered_words = defaultdict(list)
#Function to replace accents in how common the letters are
def replace_accented_letters(word):
return ''.join(accented_to_unaccented.get(char, char) for char in word) #This will parse all the words in the file and replace the first char in the dict (the accented one) with the solution
# Open and read the file
with open(file_path, 'r', encoding='utf-8') as file:
# Read each line (word) in the file
for line in file:
if len(line) > 10:
continue
# Strip any leading/trailing whitespace, force lowercase and check the word length
word = line.strip().lower()
word = replace_accented_letters(word) #Call function to replace accents
filtered_words[''.join(sorted(word))].append(word)
def find_longest_word(letters, word_list: dict):
longest_word = []
# Iterate over lengths from len(letters) down to 1
for length in range(len(letters), 0, -1):
for sample in combinations(letters, length): # Filter word_list to include only words of
sorted_letters = ''.join(sorted(sample))
matches = word_list.get(sorted_letters, None)
if matches:
return matches
return None
random_string = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz', 10))
#Use the longest word function
longest_word = find_longest_word(random_string, filtered_words)
print(f"La palabra mas larga de las letras random '{random_string}' posible era: {longest_word}")
Если вы используете комбинации букв «sorted», вам не нужно сортировать каждую выборку.
Вы уже используете Counter, вы также можете использовать его здесь:
def find_longest_word(letters, word_list):
letters = Counter(letters)
word_list.sort(key=len, reverse=True)
for word in word_list:
if Counter(word) <= letters:
return word
return ''
или
def find_longest_word(letters, word_list):
letters = Counter(letters)
return max((
word
for word in word_list
if Counter(word) <= letters
), key=len, default='')
Обратный отсчет в США? Вы имеете в виду Великобританию?