Как я могу оптимизировать проверку перестановок символов (10 букв) по списку слов?

Я пытаюсь сделать игру по телешоу 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 — это весь проект на случай, если кто-то захочет увидеть остальную часть кода.

Обратный отсчет в США? Вы имеете в виду Великобританию?

no comment 01.07.2024 13:38
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Пара вещей с точки зрения алгоритма может вам помочь....

Если вы придерживаетесь имеющегося у вас кода, вам следует создать 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», вам не нужно сортировать каждую выборку.

no comment 01.07.2024 13:47

Вы уже используете 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='')

Попробуйте это онлайн!

Другие вопросы по теме