Комментарии 29
Пример запроса из 20-ти букв (на сайтах в сети, которые предоставляют похожие сервисы):
1) поискслов.рф/anagram/search?query=абвгдежзиклмнорпстуф
Найдено: 155 страниц слов (размещения с повторениями).
Время ответа сервера: менее одной секунды на страницу.
2) wordhelp.ru/comb/абвгдежзиклмнорпстуф
Найдено: 9736 слов.
Время ответа сервера: 4 секунды.
Вопрос: Как можно обработать 4,180,411,311,071,440,000 комбинаций за 1-4 секунды? И по всем этим словосочетаниям сделать поиск по словарю?
Конечно, вопросы сложные и нетривиальные, простых ответов не будет. Но, возможно, хотя бы получится узнать направления того куда «смотреть», что «копать»…
1) поискслов.рф/anagram/search?query=абвгдежзиклмнорпстуф
Найдено: 155 страниц слов (размещения с повторениями).
Время ответа сервера: менее одной секунды на страницу.
2) wordhelp.ru/comb/абвгдежзиклмнорпстуф
Найдено: 9736 слов.
Время ответа сервера: 4 секунды.
Вопрос: Как можно обработать 4,180,411,311,071,440,000 комбинаций за 1-4 секунды? И по всем этим словосочетаниям сделать поиск по словарю?
Конечно, вопросы сложные и нетривиальные, простых ответов не будет. Но, возможно, хотя бы получится узнать направления того куда «смотреть», что «копать»…
Смотрите.
Как вам уже сказали и пару раз скажут, построить 4*10^18 комбинаций и искать их в наборе 65000 слов — контрпродуктивно.
Значит нужно как-то отсеивать нужные слова из 65000
Отсеивать — по какому признаку? По наличию нужных букв и отсутствию тех, которых в запросе нет.
Берем словарь и для каждого слова строим вектор из 33 чисел. каждое число — количество повторений соответствующей буквы.
Эти вектора можно сохранить в базу и построить по ним индексы.
И потом select woridId from vectors where 'A' <= 1, 'Б' <=1 итп
Это если «влоб» и не пытаться написать более эффективной реализации.
Как вам уже сказали и пару раз скажут, построить 4*10^18 комбинаций и искать их в наборе 65000 слов — контрпродуктивно.
Значит нужно как-то отсеивать нужные слова из 65000
Отсеивать — по какому признаку? По наличию нужных букв и отсутствию тех, которых в запросе нет.
Берем словарь и для каждого слова строим вектор из 33 чисел. каждое число — количество повторений соответствующей буквы.
Эти вектора можно сохранить в базу и построить по ним индексы.
И потом select woridId from vectors where 'A' <= 1, 'Б' <=1 итп
Это если «влоб» и не пытаться написать более эффективной реализации.
Сама сложная задача (как это я вижу сейчас) — сгенерировать эти самые 4.1804 E+18 комбинаций (за разумное время и минимально возможными ресурсами процессора).
А как их проверить — можно будет потом уже придумать (алгоритм составить или закешировать).
А если на Си (или ином языке программирования, каком?) написать перебор комбинаций, быстрее будет работать? Насколько быстрее, нежели на PHP? Не сталкивался с такими задачами раньше, хочется посоветоваться.
А как их проверить — можно будет потом уже придумать (алгоритм составить или закешировать).
А если на Си (или ином языке программирования, каком?) написать перебор комбинаций, быстрее будет работать? Насколько быстрее, нежели на PHP? Не сталкивался с такими задачами раньше, хочется посоветоваться.
Вы неправильно разбили задачу на подзадачи.
Не нужно генерировать комбинации.
Вообще.
Никак.
Нам не важен порядок букв в словах.
Нам важны множества букв и именно множества букв мы будем сравнивать.
Словарь необходимо хранить в предобработанном виде. (см мой комментарий)
Введенное пользователем слово преобразуем в описание множества букв.
Вот пример ваш со словом «мартенсит»:
мартенсит = {аеимнрстт}
Весь наш словарь уже представлен в таком виде (в виде множества букв)
И нам не нужно искать среди слов сгенерированные слова — нам нужно найти все множества которые являются подмножеством множества {аеимнрстт}
Как хранить множества в памяти и как их сравнивать — вопрос реализации, да
Не нужно генерировать комбинации.
Вообще.
Никак.
Нам не важен порядок букв в словах.
Нам важны множества букв и именно множества букв мы будем сравнивать.
Словарь необходимо хранить в предобработанном виде. (см мой комментарий)
Введенное пользователем слово преобразуем в описание множества букв.
Вот пример ваш со словом «мартенсит»:
мартенсит = {аеимнрстт}
Весь наш словарь уже представлен в таком виде (в виде множества букв)
И нам не нужно искать среди слов сгенерированные слова — нам нужно найти все множества которые являются подмножеством множества {аеимнрстт}
Как хранить множества в памяти и как их сравнивать — вопрос реализации, да
Это именно то, что я искал.
О таком направлении я не думал вообще.
Спасибо вам огромное за идею и участие!
Буду пробовать реализовать подобный алгоритм.
О таком направлении я не думал вообще.
Спасибо вам огромное за идею и участие!
Буду пробовать реализовать подобный алгоритм.
Переделал систему по предложенной вами схеме (ищем слово в индексе из 65000 слов словаря). Теперь все летает!
Запрос из 20-ти символов выполняется с следующими результатами:
Память: 12.02 megabytes
Время: 0.500537 сек.
Сам запрос:
Т.е. все буквы, которых нет в слове включаем в запрос через отрицание, а в результате получаем множества, которые состоят из нужных нам букв.
Структура таблицы vector:
Запрос из 20-ти символов выполняется с следующими результатами:
Память: 12.02 megabytes
Время: 0.500537 сек.
Сам запрос:
SELECT v.vocabulary_id, v.vocab
FROM vocabulary v
LEFT JOIN vector vkt ON v.vocabulary_id = vkt.id
WHERE `vkt`.`ye` = 0 AND `vkt`.`y` = 0 AND `vkt`.`kh` = 0 AND `vkt`.`ts` = 0 AND `vkt`.`ch` = 0 AND `vkt`.`sh` = 0 AND `vkt`.`shch` = 0 AND `vkt`.`mgk` = 0 AND `vkt`.`yi` = 0 AND `vkt`.`tvd` = 0 AND `vkt`.`ee` = 0 AND `vkt`.`yu` = 0 AND `vkt`.`ya` = 0
Т.е. все буквы, которых нет в слове включаем в запрос через отрицание, а в результате получаем множества, которые состоят из нужных нам букв.
Структура таблицы vector:
CREATE TABLE `vector` (
`id` int(11) NOT NULL,
`a` tinyint(1) NOT NULL DEFAULT '0',
`b` tinyint(1) NOT NULL DEFAULT '0',
`v` tinyint(1) NOT NULL DEFAULT '0',
`g` tinyint(1) NOT NULL DEFAULT '0',
`d` tinyint(1) NOT NULL DEFAULT '0',
`e` tinyint(1) NOT NULL DEFAULT '0',
`ye` tinyint(1) NOT NULL DEFAULT '0',
`zh` tinyint(1) NOT NULL DEFAULT '0',
`z` tinyint(1) NOT NULL DEFAULT '0',
`i` tinyint(1) NOT NULL DEFAULT '0',
`y` tinyint(1) NOT NULL DEFAULT '0',
`k` tinyint(1) NOT NULL DEFAULT '0',
`l` tinyint(1) NOT NULL DEFAULT '0',
`m` tinyint(1) NOT NULL DEFAULT '0',
`n` tinyint(1) NOT NULL DEFAULT '0',
`o` tinyint(1) NOT NULL DEFAULT '0',
`p` tinyint(1) NOT NULL DEFAULT '0',
`r` tinyint(1) NOT NULL DEFAULT '0',
`s` tinyint(1) NOT NULL DEFAULT '0',
`t` tinyint(1) NOT NULL DEFAULT '0',
`u` tinyint(1) NOT NULL DEFAULT '0',
`f` tinyint(1) NOT NULL DEFAULT '0',
`kh` tinyint(1) NOT NULL DEFAULT '0',
`ts` tinyint(1) NOT NULL DEFAULT '0',
`ch` tinyint(1) NOT NULL DEFAULT '0',
`sh` tinyint(1) NOT NULL DEFAULT '0',
`shch` tinyint(1) NOT NULL DEFAULT '0',
`mgk` tinyint(1) NOT NULL DEFAULT '0',
`yi` tinyint(1) NOT NULL DEFAULT '0',
`tvd` tinyint(1) NOT NULL DEFAULT '0',
`ee` tinyint(1) NOT NULL DEFAULT '0',
`yu` tinyint(1) NOT NULL DEFAULT '0',
`ya` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Однажды делал такую же игру. Также начинал с генерации комбинаций. На 12-и или 13-и буквах программа искала слова около часа, что неприемлемо. Оказалось, что достаточно отсекать от словаря все неподходящие слова. Вот пример похожего поиска: bit.ly/2FuZT8G. Написан на C# (с PHP не сильно знаком), но в комментариях к коду постарался описать принцип работы.
Сейчас алгоритм работает следующим образом:
1) Генерируются блоки комбинаций (размер блока: 50000-100000 слов) (66% времени на итерацию).
2) Этот блок проверяется на наличие в словаре (34% времени на итерацию).
Далее цикл повторяется…
Т.е. оптимизировать надо и первый шаг и второй.
1) Генерируются блоки комбинаций (размер блока: 50000-100000 слов) (66% времени на итерацию).
2) Этот блок проверяется на наличие в словаре (34% времени на итерацию).
Далее цикл повторяется…
Т.е. оптимизировать надо и первый шаг и второй.
С таким количеством возможных комбинаций легче просматривать все 65000 слов и проверять подходят ли они под введенное слово.
Все равно, потребуется сравнить 65000 слов и 4.1804 E+18 вариантов.
Базу из 65000 слов можно полностью закешировать.
Задача разлагается на две подзадачи:
1) Сгенерировать 4.1804 E+18 комбинаций.
2) Проверить сгенерированные комбинации в словаре из 65000 слов.
Базу из 65000 слов можно полностью закешировать.
Задача разлагается на две подзадачи:
1) Сгенерировать 4.1804 E+18 комбинаций.
2) Проверить сгенерированные комбинации в словаре из 65000 слов.
некоторые слова некоректны и нужно отбрасывать. Например, при наборе api.combination.cf/web/words/%D0%90%D0%91%D0%92%D0%93%D0%94%D0%95%D0%96%D0%97%D0%98:
1. api.combination.cf/web/description?word=%D0%B1%D0%B5
2. е и ё — одинаковы api.combination.cf/web/description?word=%D0%B5%D0%B6
1. api.combination.cf/web/description?word=%D0%B1%D0%B5
2. е и ё — одинаковы api.combination.cf/web/description?word=%D0%B5%D0%B6
Да, конечно. Когда наберется достаточная статистика и пропарсятся все доступные словари (например, сейчас в базе не хватает современных слов, вошедших в язык за последнее десятилетие) — можно будет «ручками» или автоматически пройтись по словам, откорректировать их.
Кстати, у вас на страницах «Значения слова» неправильно написан page title. Сейчас там «еж — значание слова».
Ссылка: api.combination.cf/web/description?word=%D0%B1%D0%B5
Ссылка: api.combination.cf/web/description?word=%D0%B1%D0%B5
Думаю, что в сети уже должны быть словари, в которых у слова можно выбрать следующие параметры: имя нарицательное, существительное, именительный падеж, единственное число. Затем этот словарь загружаем в свою базу данных, ибо читать с файлика каждый раз слова… ну это так себе. Затем строим индексы по количеству букв. Затем пишем запрос и вуаля: готова выдача всех возможных комбинаций слов.
В интерфейсе я бы добавил для начала сортировку уже отгаданных слов по количеству букв и по алфавиту. Еще лучше (я так думаю) выводить слова в столбцах по количеству букв.
В интерфейсе я бы добавил для начала сортировку уже отгаданных слов по количеству букв и по алфавиту. Еще лучше (я так думаю) выводить слова в столбцах по количеству букв.
Спасибо за идеи.
Над пользовательским интерфейсом ещё работать и работать.
Ещё одна интересная фишка (увидел её в аналогичных сервисах): «получить подсказку».
Т.е. при нажатии на кнопку выводится в модальном окне толкование одного или нескольких неразгаданных слов.
Над пользовательским интерфейсом ещё работать и работать.
Ещё одна интересная фишка (увидел её в аналогичных сервисах): «получить подсказку».
Т.е. при нажатии на кнопку выводится в модальном окне толкование одного или нескольких неразгаданных слов.
В Сети есть словари русского языка в текстовом формате:
www.speakrus.ru/dict
www.speakrus.ru/dict
И ещё один источник:
www.knigitxt.com/category/diction/1
www.knigitxt.com/category/diction/1
Сейчас после успешного или неуспешного набора слова фокус уходит из поля ввода. Чтобы начать вводить следующее слово, приходится либо табом фокус листать, либо мышь брать. А хочется: ввести слово — нажать Enter, ввести слово — нажать Enter и т.д. Было бы очень приятно, если бы фокус оставался в поле ввода.
В дизайне UI заложена проблема изначально. Видимо из-за попытки попробовать React и получилось, что-то вроде (зато работает). Ушел от Jquery как писал в статье, но все равно добавил в проект. И даже имея Jquery, создал статические страницы с submit всей страницы саму на себя. За попытку молодец. Но… в погоне за React делаем откат лет на 10 назад.
Говорим об оптимизации и вместо чистого запроса нового слова, перегружаем каждый раз страницу на POST снова генерим ее клиенту. В итоге чтобы побороть неудобство фокуса, добавим костыль с установкой фокуса после отрисовки страницы. И так далее, с ростом UI, костыли будут только рости.
Напоминает попытку, перфоратором закручивать шурупы… да это возможно, но вроде и глупо.
Говорим об оптимизации и вместо чистого запроса нового слова, перегружаем каждый раз страницу на POST снова генерим ее клиенту. В итоге чтобы побороть неудобство фокуса, добавим костыль с установкой фокуса после отрисовки страницы. И так далее, с ростом UI, костыли будут только рости.
Напоминает попытку, перфоратором закручивать шурупы… да это возможно, но вроде и глупо.
На React JS написан отдельный сайт: js.combination.cf
Но там функционал обрезанный (по сравнению с полной версией api.combination.cf/web), есть ряд особенностей связанных с хостингом (локально работает — на хостинге «нет»).
Разрабатывать удобный, современный и функциональный UI — мне этому ещё учиться и учиться. Пока в приоритете бекэнд (60-70% усилий), на фронтенд — оставшиеся 30-40%.
Но там функционал обрезанный (по сравнению с полной версией api.combination.cf/web), есть ряд особенностей связанных с хостингом (локально работает — на хостинге «нет»).
Разрабатывать удобный, современный и функциональный UI — мне этому ещё учиться и учиться. Пока в приоритете бекэнд (60-70% усилий), на фронтенд — оставшиеся 30-40%.
Сделал ряд доработок по интерфейсу:
1) Фокус автоматически ставится в поле ввода (после загрузки страницы).
2) Добавил на гл. страницу форму с случайными словами, в которые можно поиграть, перейдя одним кликом по кнопке.
3) Описание отгаданного слова в игре теперь выводится в модальном окне (без перезагрузки страницы).
4) Почищена база слов (от глаголов, прилагательных, наречий, союзов и т.п.)
5) Добавлен функционал «Получить подсказку».
6) Разные мелкие правки текстов, описаний, подсказок, заголовков и т.п.
1) Фокус автоматически ставится в поле ввода (после загрузки страницы).
2) Добавил на гл. страницу форму с случайными словами, в которые можно поиграть, перейдя одним кликом по кнопке.
3) Описание отгаданного слова в игре теперь выводится в модальном окне (без перезагрузки страницы).
4) Почищена база слов (от глаголов, прилагательных, наречий, союзов и т.п.)
5) Добавлен функционал «Получить подсказку».
6) Разные мелкие правки текстов, описаний, подсказок, заголовков и т.п.
Поиграл немного, с фокусом стало очень приятно. Выбрал предложенное слово «Каменистость» и принес вам новых фактов насчет словаря:
1) В словаре отсутствуют: оттиск, насест, тост, кастет, тать, нить, темнота.
2) Наречия и глаголы все еще присутствуют в словаре: мести, намести, никто, таки, есть.
3) Подсказки иногда показывают слово в другом падеже, например, подсказка для слова «намек» содержит пример «ни намека на...». Для слова кистень — «разбойник с кистенем». Еще сок, сонет, сан.
Еще поддержу мысль, что выше изложил JuniorNoobie: сортировка отгаданных слов была бы очень уместна. Успехов вам!
1) В словаре отсутствуют: оттиск, насест, тост, кастет, тать, нить, темнота.
2) Наречия и глаголы все еще присутствуют в словаре: мести, намести, никто, таки, есть.
3) Подсказки иногда показывают слово в другом падеже, например, подсказка для слова «намек» содержит пример «ни намека на...». Для слова кистень — «разбойник с кистенем». Еще сок, сонет, сан.
Еще поддержу мысль, что выше изложил JuniorNoobie: сортировка отгаданных слов была бы очень уместна. Успехов вам!
Начал тестировать — залип)
Спасибо, давно не играл!
Спасибо, давно не играл!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Опыт веб-разработки при создании игры «Составь слова»