Как сделать мур

Добавил пользователь Morpheus
Обновлено: 01.09.2024

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

Известный автор книг по занимательной математике Е. Я. Гик в своей книге "Занимательные математические игры" опубликовал такое 16-ходовое решение, как из мухи сделать слона: муха-мура-тура-тара-кара-каре-кафе-кафр-каюр-каюк-крюк-урюк-урок-срок-сток-стон-слон.

И вот, в один прекрасный день мне довелось заняться решением этой задачи в программном виде.

Из мухи слона, первая версия

Честно признаться, слона из мухи получилось сделать довольно быстро.

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

1) Словарь существительных

Оказалось, даже с первым пунктом есть проблемы, — найти стоящий словарь существительных оказалось уже отдельной подзадачей. Не помню где именно, но нашёл сносный готовый словарь. Формат по одному слову на строку, utf8, разделители \r\n — так и оставил в дальнейшем.

2) Алгоритм

Действительно, если в качестве алгоритма использовать построение дерева со всеми вариантами, пока при прокладке очередного уровня не попадётся искомое слово, то никаких ресурсов не хватит.

Даже если дать очень оптимистичную оценку на среднее число вариантов модификаций в 5 (всего!), то если к какому-то слову минимальный путь составит 10 шагов, то в памяти должно уместиться дерево в 5 10 ~= 10 млн нодов. Учитывая накладные расходы на содержание структуры дерева (как минимум, 2 указателя на потомков из предка каждый по 4/8 байт) и на собственно хранение данных нод (языковая/структурная обёртка переменной + сами данные: символы строки в utf8 ещё более 10 байт) получим требование по ОЗУ для таких условий минимум порядка 200-300 Мб. А условия могут быть гораздо хуже.

Для простоты и недолго думая, соорудил простейший кэш прямо в функции оценки.

Поиск новых вариантов мутаций идёт по словарю и подсловарям, отталкиваясь от заданного слова. При этом есть несколько дополнительных логических ограничений.

Параметр длины слова — это я на спичках mb_strlen поэкономил. Так-то метод задумывался приватным, но для проб и тестов был опубличен. Не пускайте такие штуки в продакшн :) Во всяком случае, без охватывающих проверок.

А конечное слово… может человеческая рефлексия какая-то, а может и интуиция, — оставил возможность использования на потом. Всё же логично ожидать от функции получения набора потомков какой-то зависимости от того, на кого похожими они должны получаться. Ничто не мешает делать первичный отсев прямо тут. Но пока — не используется.

3) Работа со словарём

Алгоритм хорошо, но у нас есть источник данных (слов) ввиде файла, с которым надо эффективно и много работать из алгоритма поиска.
Да, этот файл-словарь отсортирован по алфавиту по возрастанию. И он не такой уж гигантский, всего около 1 Мб, так что мы можем его смело загрузить для работы в оперативку целиком.

[Здесь же первая мысль о логичности использования БД. Но решил попробовать сделать сначала без неё.]

3.1) Быстрый поиск по сортированному массиву первого из неуникальных значений

Следует заметить, что эта версия бинарного поиска самая обычная, арифметическая, пригодная для работы в сортированном массиве с последовательной целочисленной нумерацией (ключи от 0 до N-1 например).

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

Пример: ищем МУА, есть массив (см. ниже) [… 99-МС(т)ИТЕЛЬ, 100-МУ(з)А, 101-МУ(к)А, 102-МУ(р)А, 103-МУ(т)А, 104-МУ(х)А, 105-МУ(р)АВЕЙ, 106-МУ(р)АВЕЙНИК… ] Обычный бинарный поиск попадает очередной итерацией допустим попадает в ключ 102. Значение элемента (МУА, получилось из слова МУРА) равно искомому (МУА, ищем потомков для МУХА) и этот ключ нам и пришёл бы. И потом загромождай логику перебором и вверх и вниз. Модифицированный алгоритм находит именно самый первый, ключ 100, и далее можно идти последовательно вниз по массиву, пока элемент == искомое.

3.2) Вспомогательные словари псевдослов

Поскольку перебор идёт по ограниченной части словаря, где при старте первая позиция определяется бинарным поиском, уже при трёх подсловарях перебор при рассмотрении выбивания 4-й буквы и старше уже не является фатально узким местом.

Приемлемым компромиссом по память/скорость вышло использование 3-4 подсловарей.

Конечно, абсолютно логично что 5-й подсловарь (где из слов убрана 5-я буква и получившееся пересортировано) для превращения 4-буквенных мухи и слона не нужен, и является только обузой. Но посмотрим на другом примере:

Т.е. 5-й подсловарь можно добавлять разве что после оптимизации хранения словарей, кэширования, алгоритма. А сейчас он только лишнее потребление оперативки.

слон муха

Версия вторая

Но интересно не это. Самые интересные изменения тут это парсер, фактор случайности и функция оценки, основанная на частотных характеристиках букв.

1) Парсер

Я заметил что исходный словарь хоть и достаточно большой, но там почему-то нет даже некоторых общеупотребительных слов. И внимание ))) там не было слона! Слоненок был, слониха была, а слона упс. Дискриминация.

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

[И да, в этом словаре я наткнулся в первый раз (из последующих шишек php sort, несмотря на верную локаль для setlocale и mb_string) что слова на Ё, внезапно, были в конце словаря.]

Но тут помог великий вебархив, за что ему спасибо.

2) Фактор случайности

Есть конечно и обратная сторона — для неудобных пар бывает поиск и не находит цепочки. Или находит несколько длиннее обычной. Но всё же основной случай достаточно стабилен.

3) Функция оценки

Из МУХА СЛОН получался довольно живенько и симпатично, в пределах 10 переходов.
Но (!)
из МУХА слово СЛОГ получалось… упрямо переходов в 60-70 (!).
А ведь очевидно, что должно бы быть всего на 1 длиннее чем в СЛОНа. Человеку очевидно. Машине нет, она по алгоритму. Ошибка алгоритма. Ошибка функции оценки.

Экспирементировал немало, не скрою.

Получалось ну на 5 позиций укоротить цепочку при сомнительных изменениях в разбалловке. Но это же не тот результат который нужен.
Очевидно, с лёту с корректировки проблема не решалась, думал. В чём дело. В чём разница. Разница в последней букве, да, факт. Там слоН, а тут слоГ. Чем же эти буквы так отличаются, что всё так плохо…

Взял таблицу частот употребления букв из вики. (На самом деле это не совсем корректно, надо по имеющемуся словарю существительных частоты считать, но вряд ли бы были кардинальные различия). Вбил как есть в код. Это не очень красиво, да, но это пока и пусть. Раскроил частоты букв на нормированные массивы по гласным и по согласным, с нормировкой по максимально употребительной гласной/согласной. Переписал функцию оценки. ЕСТЬ! СЛОН + 1!

Да и сам СЛОН стал получаться ещё на шаг-другой быстрее.

Как видим, потяжелел словарь (с 0,68 Мб до 1,03 Мб, +51%), а с ним подсловари и итоговый жор оперативки. Комбинаторика улучшилась, и хоть и на каждом шаге мутаций стало рассыпаться больше, а значит шагать стали медленнее, — конечная цель, при достижимости, стала получаться по результату быстрее, за меньшее число шагов.

Однако, по времени поиск получился совсем не быстрее, даже для 4 подсловарей. Один фактор другой не компенсировал. Тем не менее, по сути расширить словарь абсолютно корректный и необходимый ход для приближения к реальным боевым условиям. И есть ещё множество справочников кроссвордиста и словарей, в том числе онлайновых, с которыми можно поработать для расширения нашего словаря.

*
* Вообще, эта задача кажется бесконечной.
* И в этой долгой дороге к совершенству,
* Пожалуй, на этом пятачке стоит сделать ещё один перекур.
*


Некий деятель науки
ДЕЛАТЬ стал СЛОНА из МУХИ:
Надувал, надувал,
Поглядеть народ позвал.

Удивить он всех хотел…
Ну а слон-то улетел!

Версия третья

Честно признаться, ожидал от версии большего. Всё-таки база (была под рукой MySQL 5.5) должна, ну должна же была обеспечить взлёт хотя бы в разы, если не на порядки.

1) База и скорость?

Судя по всему, в версии с файлами я фактически добился эффекта memcache — куча словарей в памяти, а алгоритм, в общем, един и для файл- и для mysql-версий. В построении базы вроде соображаю, все нужные индексы для работы были проставлены.

Так или иначе, ответ за 1 сек лучше чем за 5 сек.

2) Кэширование очевидного узкого места

Изначально, правда, было под 2 сек, из-за обильных запросов SELECT слово ПО ид. Агрессивное кэширование в MySQL-маппере устранило эту проблему. Тоже конечно не оптимальным образом, но и это ещё далеко не хайлоад продакшн, а эксперимент. Вполне терпимо на данном этапе.

Скрипт при этом не потребляет так конски оперативку, как в версии с чисто PHP и файлами словарей (под 100 Мб), потребление самое обычное. Те же данные, хранящиеся в СУБД, ведут себя более пристойно по аппетитам к памяти.

Что дальше?

Безусловно, решение не идеально, я знаю. Многое ещё можно сделать:

  • Вместо одного процесса поиска от начального к искомому, сделать агоритм на двух параллельных процессах поиска: от начального к искомому и от искомого к начальному, с выходом и построением цепочки по первой коллизии пары слов из двух процессов. Насколько я знаю алгоритмы заливки, таклй ход помогает улучшить скорость получения результата в 2-4 раза. Да, генетический алгоритм другой случай, но есть ощущение что вот такое встречное движение и тут даст результат.
  • Сделать горизонтальное масштабирование словаря? Раскидать слова разной длины по разным подтаблицам. Для этой задачи это допустимый ход. Ввиде дополнительного поля длины слова и индекса по нему пробовал, — ничего. Значит только партиционирование. Будет ли от этого толк, впрочем, пока затрудняюсь сказать.
  • Redis? Memcached?
  • Распараллеливание процессов обсчёта поколений генетических мутаций до N штук параллельных процессов, в зависимости от числа ядер на сервере
  • Добавить юзер френдли? В цепочках попадаются такие слова, о которых и не слыхивал. Интересно бы в цепочке на клиенте показывать и значение этих слов.
  • CP1251? Utf-8 это безусловно прекрасно. Но если работать заведомо только с русскими словарями, или же в сущности словаря указывать кодировку, в которой на самом деле хранятся слова, то почему бы и нет. Строгий 1 байт сильно упрощает работу со строкой для железок, и в 2 раза меньше будет кушать памяти. Это явно неплохо.
  • JavaScript версия? В случае массового количества запросов, например хабраэффекта, это неплохая идея — зачем сервер такими вычислениями нагружать, пусть железо на клиенте пыль стряхнёт, кулеры погоняет.
  • Серверная версия на C++?
  • И наверняка другие ходы, которые пока ещё не приходили в голову.

PS:
Конечно, в этой статье нельзя не оставить ссылку на то, как делают из мухи слона художники.

Комментарии и замечания приветствуются! Может упустил какие-то простые и важные вещи. Благодарю за внимание.


Меч стреляет быстрыми снарядами в виде голов котиков, которые отскакивают от блоков и пробивают 5 целей. Снаряды подвержены гравитации, они оставляют после себя радужный след и мяукают при отскакивании. Снаряд отскакивает 4 раза и на пятый исчезает. Является вторым по силе мечом в игре. Очень полезен в закрытых помещениях, например в данже и пещерах.

Мяумур в Террарии

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

Смертоносное оружие достанется игроку, если тот долго сможет выжить в мире Хардмод. Это оружие ближнего боя с автоматической атакой. Игроки могут просто удерживать кнопку атаки, а меч будет продолжать стрелять.

Как получить Мяумур


Мяумур чрезвычайно редок, так как есть только один способ сделать это: победить Лунного Лорда и надеяться, что он выпадет из него. Лунный Лорд — последний босс в Террарии. Чтобы добраться до этого места, необходимо пройти всю игру, победить Культист-Лунатик и пройти Лунные События.

Его невозможно скрафтить, а только выбить.

Если удастся победить Лунного Лорда, с вероятностью 11,11%, или 1/9 шанса, Мяумур можно выбить из босса.

Вид меча


Очень сложно спутать Мяумур с другими мечами в игре. Оружие имеет розово-синий клинок, а у его основания — кошачья морда. Снаряд кошки выпускаемый из меча может отскочить примерно четыре раза и исчезнет.

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

История меча Мяумур


Мяумур был впервые введен в версии 1.3 как часть новых функций и элементов для игроков. Добавление меча было разрекламировано разработчиками в финальных титрах мобильной игры 1 апреля.

Статистика


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

Несмотря на снижение максимального урона, снаряд очень быстрый, так как может разгоняться до 100 км в час. Скорость и количество взрывов увеличивается, когда кошка отскакивает от стены. Отбрасывание установлено на 6.5, что является сильным. Базовый шанс критического удара на уровне 4%.

Модификаторы


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

Сравнение с другим оружием


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

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

Использование в крафте


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

Наряду с Мяумуром, для создания Зенита необходимы Терра-меч, Звездный гнев, Колебание притока, меч Всадника, Сеятель, Звездная ярость, Пчеловод, Зачарованный меч и Медный короткий меч. Зенит можно создать на мифриловой или орихалковой наковальне.

Чекеры: Чекер проверяет ваши базы на наличие каких либо игр, или нужной вам информации на аккаунте
Он может быть под любые запросы, как под игровые так под порно сайты
Чекеры на ваш запрос можно найти на форуме

Чекеры на подобие P/K, UBC, OpenBullet
Они отличаются от обычных чекеров тем что, в самом софте можно написать свой запрос, (Нужный вам сайт), или же скачать бесплатные проекты для софта

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

Сколько стоили презервативы в Москве 1940-х годов и в чем главный секрет знаменитых пожарских котлет из нежной курицы? Эти и многие другие любопытные факты можно почерпнуть из документального романа Сергея Белякова, посвященного сыну Марины Цветаевой, а также из рецензии Константина Митрошенкова на эту книжку.

Сергей Беляков. Парижские мальчики в сталинской Москве. М.: Редакция Елены Шубиной, 2021. Содержание. Фрагмент


Георгий Эфрон приехал в Москву из Парижа в июне 1939 года вместе с матерью, Мариной Цветаевой. За два года до этого в СССР перебралась сестра Георгия Ариадна и его отец Сергей Эфрон, работавший на советскую разведку. Основной причиной смены места жительства стала нелегальная деятельность Сергея Эфрона, которая привлекла внимание французских властей и подпортила репутацию семьи в эмигрантском сообществе.

Переехав в СССР, юноши вскоре затосковали по парижской жизни и стали мечтать о возвращении во Францию. Беляков замечает, что Мите удалось то, что не смог сделать Мур, — спустя много лет снова оказаться в стране, которую он считал своей родиной:

Значительную часть 600-страничной книги Белякова составляют долгие и утомительные экскурсы в реалии предвоенного СССР. Например, рассказывая о том, как Мур и Цветаева перевозили вещи со своей дачи в Голицыне в московскую квартиру, Беляков сообщает нам 1) какая организация предоставляла услуги грузоперевозок; 2) где в Москве располагались стоянки грузовых такси; 3) сколько стоил заказ грузового такси от Белорусского вокзала до Голицына (58 рублей 80 копеек, если вам вдруг интересно).

Мне могут возразить, что все эти подробности очень важны — ведь это исторический контекст, который позволяет нам лучше понять героев и те обстоятельства, в которых они действовали. Проблема в том, что контекст любого события практически неисчерпаем, и поэтому автору — пишет ли он роман или историческое исследование — важно уметь отделить существенные подробности от несущественных. Беляков же настолько увлечен советской повседневностью 1940-х годов, что его книга превращается в собрание занимательных, но бесполезных фактов. Самое главное, что на этом фоне теряется трагедия семьи Георгия, мать которого покончила с собой вскоре после начала войны, отец был расстрелян в тюрьме, а сестра провела восемь лет в лагерях.

Процитированное письмо датировано 14 июня 1944 года. 7 июля того же года Мур был ранен в бою за деревню Друйка (ныне Витебская область, Беларусь) и пропал без вести. Исследователи предполагают, что в санитарный автомобиль, на котором его увезли с передовой, попала бомба. На тот момент Муру было всего 19 лет.

Читайте также: