fecka97 Ответов: 1

Помогите мне ускорить мой код/алгоритм Python


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

Так или иначе, задача была следующая:

У меня есть x сумма (в данном случае 24) буквы и список слов.
Я должен был найти все это. сочетание слов("предложения"), которые используют данные буквы с помощью ни одного письма не осталось.

Обновление: Слово может появиться в предложении более одного раза, например "the the the" - это прекрасно, если буквы позволяют это сделать.

Например:

вход:

данное письмо:
IFSNIHLSTA

И список слов:
nice
guys
finish
last

(Максимальное количество слов, которое мы ищем, в данном случае не имеет значения.)

Выход есть:
finish last
last finish


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

Что я уже пробовал:

То самое первое, что делать было очевидно, чтобы сократите количество слов во-первых, поэтому я отфильтровал те, которые точно не нужны.

Затем первый основная идея он должен был взять слово, использовать буквы для него, затем взять следующее слово и использовать оставшиеся буквы, и так далее, пока все буквы не будут израсходованы, или больше слов не может быть создано. Если все буквы израсходованы, то ура, мы получили предложение.

Повторение вышеописанного процесса для всех слов и снова для каждого слова(после действительного) в конце концов даст нам все комбинации, верно?

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

Поэтому я искал способы сделать его достаточно эффективным, чтобы его можно было использовать (если бы я добился успеха, я бы не писал это):

Я решил, что, поскольку я ищу только комбинации, порядок не имеет значения, поэтому у меня гораздо меньше слов для начала (если я ищу предложения из 6 слов, то мне нужно смотреть только на 1/6 слов для первой итерации).

Реализация это действительно спагетти-код, так как еще день назад я даже не знал, как выглядит python, извините за это.

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

Так как это около 200 строк, я положил его на pastebin вместо того, чтобы вставить его сюда:
Код на Pastebin[^]

У меня есть 2 вида вопросов:

Теоретический:
Как улучшить алгоритм или какие другие алгоритмы использовать, которые могли бы быть более эффективными? (и еще, что я упускаю?)

Обновление: В выходных данных все еще есть несколько дубликатов, и мне нужно будет найти способ их удалить.

О самом спектакле:
Несмотря на то, что каждый процесс работает на собственном потоке процессора, и нет никакого дискового ввода-вывода, процессоры не используются на 100%, почему бы и нет?

Кроме того, продвинулся бы я дальше с CUDA или openCL, если да, то как?

Заранее спасибо

Patrice T

Покажите пример ввода/вывода.

fecka97

Пример, который я только что собрал:

Ввод:

Даны буквы: "IFSNIHLSTA"

И список слов:

милый
парни
заканчивать
последний

(Максимальное количество слов, которое мы ищем, в данном случае не имеет значения.)

Выход есть:

- финишируй последним"
"последний финиш"

Эти слова вместе используют точные данные буквы.

Я тоже собираюсь обновить этот вопрос.

Patrice T

насколько велик список слов?
Используется ли он повторно или каждый раз новый список?
При повторном использовании вы можете хранить дополнительную информацию с помощью слов?

fecka97

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

Сейчас это просто список(или массив, если хотите) строк, но хранение большего количества данных со словами не должно быть проблемой.

Кроме того, я забыл упомянуть, что одно слово может встречаться в предложении более одного раза, так что "the the the" может быть абсолютно возможным, если буквы позволяют это сделать.

Также в текущей версии дубликаты являются проблемой, например "asd bsd csd" и "asd csd bsd" считаются дубликатами. Увеличение количества просматриваемых слов (... listSize/3, listSize/2, listSize/1) решило бы эту проблему, но это не сработает, если максимальное количество слов для предложения неизвестно, и это не позволит одному и тому же слову появиться более одного раза.

Patrice T

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

fecka97

Спасибо, я уже сделал это.

1 Ответов

Рейтинг:
5

Patrice T

Общий ответ: когда вы думаете о производительности; оптимизация, ускорение ...
Первое что нужно сделать это подумать профилировщик.
26.4. Профилировщики для Python — питон 2.7.14 документации[^]
python - как вы можете профилировать скрипт? - переполнение стека[^]
Профилировщик - это инструмент, который покажет вам, сколько времени вы тратите в своем коде и где оно тратится. Он также сообщает вам, сколько раз выполняется данная часть.
Место, которое потребляет больше всего времени, - это первое место, на которое следует обратить внимание для оптимизации.
И везде, вы должны спросить себя:
- Зачем я это делаю?
- Это действительно необходимо?
- Могу ли я сделать это другим способом, который менее ресурсоемкий?

У вас есть файл ~500 тысяч слов, этот файл более или менее постоянен. Это означает, что любая работа, которую можно сделать заранее и сохранить в файле, будет огромной экономией.

letters.find(char.upper())

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

Совет: не используйте резьбу при оптимизации, если обработка файла слишком длинная, используйте уменьшенную для целей тестирования.


fecka97

Спасибо! Этот профайлер и Ваши советы уже очень помогли мне.

Я думаю, что пройдусь по коду и упростю его настолько, насколько смогу. Теперь, когда я знаю, что это действительно работает, мне не нужен регистратор и отпечатки повсюду.

Кроме того, не могли бы вы дать намеки на другие подходы, если они у вас есть? На данный момент я могу только уточнить этот вариант, но боюсь, что в конце концов он все равно потребует слишком много итераций, чтобы его можно было использовать (вот почему я думал об openCL и большем количестве потоков).

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

Patrice T

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

fecka97

Хорошо, я взял меньший набор данных, который занял примерно 10 секунд для обработки, после нескольких оптимизаций мне удалось довести его до 8 секунд, а с помощью Pypy это на самом деле всего 0,9 секунды.

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

В любом случае, еще раз спасибо за вашу помощь, я понял, что все остальное-это просто решение проблемы, которая зависит от меня, поэтому я закрою вопрос.