Python Интеграция многопроцессорной обработки в несколько вложенных циклов - PullRequest
0 голосов
/ 29 апреля 2020

Я делаю генератор слов скрэббл, который невероятно неэффективен из-за отсутствия у меня навыков кодирования. В этой программе пользователь вводит последовательность букв, и программа использует грубую силу, чтобы найти каждое правильное слово скрэббл. Чтобы ускорить этот процесс, я хочу реализовать многопроцессорность, но не могу заставить его работать успешно. Рабочий не многопроцессорный код ниже

from multiprocessing import Process
usrList = input("type the letters you have     ")
usrList = list(usrList.upper())
usrList.sort()
print(usrList)    


storedList = []

def word2 (usrList):
    print('trying to find two letter words')
    for i in range(0,len(usrList)):
        for j in range(0,len(usrList)):
            if i != j:
                if str(usrList[i])+str(usrList[j]) not in storedList and str(usrList[i])+str(usrList[j])+'\n' in dicList:
                    print(str(usrList[i])+str(usrList[j]))
                    storedList.append(str(usrList[i])+str(usrList[j]))

def word3(usrList):
    print('trying to find three leter words')
    if len(usrList) > 2:
        for i in range(0,len(usrList)):
            for j in range(0,len(usrList)):
                for k in range(0,len(usrList)):
                    if i != j and i != k and j != k:
                        if  str(usrList[i])+str(usrList[j])+str(usrList[k]) not in storedList and str(usrList[i])+str(usrList[j])+str(usrList[k])+'\n' in dicList :
                            print(str(usrList[i])+str(usrList[j])+str(usrList[k]))
                            storedList.append(str(usrList[i])+str(usrList[j])+str(usrList[k]))

def word4(usrList):
    print('trying to find four letter words')
    if len(usrList) > 3:
        for i in range(0,len(usrList)):
            for j in range(0,len(usrList)):
                for k in range(0,len(usrList)):
                    for l in range(0,len(usrList)):
                        if i !=j and i != k and i!= l and j!= k and j!= l and k != l:
                            if str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l]) not in storedList and str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+'\n' in dicList: 
                                print(str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l]))
                                storedList.append(str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l]))


def word5(usrList):
    print('trying to find five letter words')
    if len(usrList) > 4:
        for i in range(0,len(usrList)):
            for j in range(0,len(usrList)):
                for k in range(0,len(usrList)):
                    for l in range(0,len(usrList)):
                        for m in range(0,len(usrList)):
                            if i !=j and i != k and i!= l and i != m and j!= k and j!= l and j!= m and k != l and k != m and l !=m:
                                if str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m]) not in storedList and str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m])+'\n' in dicList:
                                    print(str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m]))
                                    storedList.append(str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m]))


def word6(usrList):
    print('trying to find six letter words')
    if len(usrList) > 5:
        for i in range(0,len(usrList)):
            for j in range(0,len(usrList)):
                for k in range(0,len(usrList)):
                    for l in range(0,len(usrList)):
                        for m in range(0,len(usrList)):
                            for n in range(0,len(usrList)):
                                if i !=j and i != k and i!= l and i != m and i != n and j!= k and j!= l and j!= m and j !=n and k != l and k != m and k != n and l !=m and l != n and m!= n:
                                    if str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m])+str(usrList[n]) not in storedList and str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m])+str(usrList[n])+'\n' in dicList:
                                        print(str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m])+str(usrList[n]))
                                        storedList.append(str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m])+str(usrList[n]))

def word7(usrList):
    print('trying to find seven letter words')
    if len(usrList) > 6:
        for i in range(0,len(usrList)):
            for j in range(0,len(usrList)):
                for k in range(0,len(usrList)):
                    for l in range(0,len(usrList)):
                        for m in range(0,len(usrList)):
                            for n in range(0,len(usrList)):
                                for o in range(0,len(usrList)):
                                    if i !=j and i != k and i!= l and i != m and i != n and i != 0 and j!= k and j!= l and j!= m and j !=n and j != o and k != l and k != m and k != n and k!= o and l !=m and l != n and l != 0 and m!= n and m != o and n != o:
                                        if str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m])+str(usrList[n])+str(usrList[o]) not in storedList and str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m])+str(usrList[n])+str(usrList[o])+'\n' in dicList :
                                            print(str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m])+str(usrList[n])+str(usrList[o]))
                                            storedList.append(str(usrList[i])+str(usrList[j])+str(usrList[k])+str(usrList[l])+str(usrList[m])+str(usrList[n])+str(usrList[o]))        



f = 'ScrabbleDic.txt'
with open(f,'r') as file:
    dicList=[]
    for line in file:
        dicList.append(line)
    file.close()

if __name__ == '__main__':
    word7(usrList)
    word6(usrList)
    word5(usrList)
    word4(usrList)
    word3(usrList)
    word2(usrList)

Ответы [ 2 ]

4 голосов
/ 29 апреля 2020

В целом, вы часто получаете больше пользы от перепроектирования вашего алгоритма, чем от многопроцессорной обработки.

Вот более короткая реализация вашего кода. Я жестко запрограммировал usrList, и, поскольку у меня нет доступа к файлу словаря, который вы используете, я использую файл словаря по умолчанию, который поставляется с MacOS. Вместо того, чтобы писать вложенные циклы и проверять дубликаты индексов, я использую модуль itertools для генерации всех перестановок usrList для заданной длины. Это существенно не ускорит код, но упростит демонстрацию возможных изменений:

import itertools

usrList = ['P', 'Y', 'T', 'H', 'O', 'N', 'S']
storedList = []
with open('/usr/share/dict/words', 'r') as dict_file:
    dicList = [word.strip().upper() for word in dict_file]


def possible_words(length):
    for letter_permutation in itertools.permutations(usrList, length):
        word = ''.join(letter_permutation)  # itertools returns a tuple, not a string
        if word in dicList:  # This requires a linear search through the list
            storedList.append(word)


for word_length in range(2, 8):  # Note that the upper bound is 7 letters, not 8
    possible_words(word_length)

Это займет около 47,4 секунд для запуска на моем Macbook. Чтобы ускорить его, давайте добавим многопроцессорность, как вы предлагаете. Есть несколько способов использовать многопроцессорность, но проще всего реализовать, вероятно, создание пула и вызов его функции map().

Этот синтаксис может выглядеть немного странно, если вы не привыкли к функциям, которые принимают другие функции в качестве аргументов. По сути, мы создаем пул работников, затем даем этому пулу функцию и диапазон аргументов для использования в этой функции. Затем отдельные вызовы функций распределяются по пулу вместо последовательного вызова:

import itertools
import multiprocessing

usrList = ['P', 'Y', 'T', 'H', 'O', 'N', 'S']
storedList = []
with open('/usr/share/dict/words', 'r') as dict_file:
    dicList = [word.strip().upper() for word in dict_file]


def possible_words(length):
    for letter_permutation in itertools.permutations(usrList, length):
        word = ''.join(letter_permutation)
        if word in dicList:
            storedList.append(word)


if __name__ == '__main__':  # multiprocessing complains if this isn't isolated
    with multiprocessing.Pool(6) as p:  # Creates 6 worker processes
        p.map(possible_words, range(2, 8))  # Each process calls possible_words() with a different input

Это выполняется за 32.3 секунд на моем Macbook. Мы сбрили четверть времени! Вероятно, есть способы выжать немного больше производительности из этого подхода, но также стоит взглянуть на алгоритм, чтобы увидеть, есть ли другие способы ускорить это.

Прямо сейчас, вы создаете список словарных слов. Когда вы проверяете, есть ли потенциальное слово в этом списке, Python должен просмотреть весь список, пока не найдет совпадение или не достигнет конца. Мой встроенный словарь содержит 235 тыс. Слов, так что это означает, что он должен сравнивать 235 тыс. Строк для каждой бессмысленной комбинации букв, которые он генерирует!

Если вы переключитесь с использования списка на набор, Python может вместо этого ищите значение в почти постоянное время, используя функцию ha sh вместо сканирования каждой записи по одному. Давайте попробуем это вместо многопроцессорной обработки:

import itertools

usrList = ['P', 'Y', 'T', 'H', 'O', 'N', 'S']
storedList = []
with open('/usr/share/dict/words', 'r') as dict_file:
    dicSet = {word.strip().upper() for word in dict_file}   # By changing [] to {}, this is now a set


def possible_words(length):
    for letter_permutation in itertools.permutations(usrList, length):
        word = ''.join(letter_permutation)
        if word in dicSet:  # This now only does 1 check, not 235,000
            storedList.append(word)


for word_length in range(2, 8):
    possible_words(word_length)

Эта версия запускается за 0,005 секунд , после замены всего двух символов!

В общем, многопроцессорная обработка является полезным инструментом, но это, вероятно, не должно быть первым, что вы попробуете. Как правило, вы получите гораздо лучшие результаты, если продумаете используемые вами структуры данных и алгоритмы, а также то, где может быть узкое место.

0 голосов
/ 29 апреля 2020

Классическое решение для решения подобных головоломок состоит в том, чтобы не проверять каждую возможную перестановку, а вместо этого преобразовывать образцы букв и слов в словаре в согласованную перестановку с возможностью поиска - сортируя их символы!

Теперь вместо поиска в словаре для каждой перестановки 'PYTHONS', вы просто сортируете буквы, чтобы создать ключ 'HNOPSTY', и все действительные слова с тем же ключом будут найдены на карте.

Используя defaultdict, это легко создать карту поиска всех слов в вашем словаре. Мы используем defaultdict(list) вместо слова, потому что несколько слов могут сортироваться по одному и тому же ключу.

from collections import defaultdict
dictionary_mapping = defaultdict(list)

# assuming dictionary is a list of all valid words, regardless of length
for word in dictionary:
    key = ''.join(sorted(word.upper()))
    dictionary_mapping[key].append(word)

search_word = "PYTHONS"
search_key = ''.join(sorted(search_word.upper()))

# get all words that are anagrams of the search word, or the empty list if none
print(dictionary_mapping.get(search_key, []))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...