Анагра?мма (от греч. ???- — «пере» и ?????? — «буква») — литературный прием, состоящий в перестановке букв или звуков определенного слова (или словосочетания), что в результате дает другое слово или словосочетаниеАнаграммы используются для шифрования ответа на вопрос викторины (шарады, кроссворда и т.п.).
Предлагаю вашему вниманию две функции, реализующие алгоритм получения анаграмм из отдельных слов и словосочетаний.
Работу функций «в боевом», как говорится, режиме можно увидеть здесь.
Для выбора наиболее «перемешанной» анаграммы применяется функция Левенштейна.
Для слов длиной в три буквы и меньше анаграмма не составляется — смысла нет.
Для чисел — то же.
Кодировка — UTF-8.
В качестве значения единственного параметра функции GetAnagramm подаём слово или словосочетание, а на выходе GetWordAnagramm получаем готовую анаграмму в которой все буквы заключены в SPAN-элементы HTML (для того, чтобы CSS-стилями придать анаграмме презентабельный вид).
//Получение анаграммы слова или словосочетания
function GetAnagramm($anagramm)
{
$out_anagramm = "";
$len_anagramm = mb_strlen($anagramm,'UTF-8');
$int_anagramm = (int)$anagramm;
if ($len_anagramm > 3 && $int_anagramm == 0)
{
mb_regex_encoding('UTF-8');
mb_internal_encoding('UTF-8');
$wordslist = preg_split('[-| ]', $anagramm);
$out_anagramm = "";
$len_anagramm = 0;
$i = 0;
foreach ( $wordslist as $value)
{
$len_anagramm = $len_anagramm + mb_strlen($value,'UTF-8');
$simbol = mb_substr($anagramm, $len_anagramm+$i, 1, 'UTF-8');
$span = "";
if ($simbol <> "") $span = "<span class=\"annagramm\">".$simbol."</span> ";
if (mb_strlen($value,'UTF-8') == 1)
{
$out_anagramm .= "<span class=\"annagramm\">".$value."</span> ".$span;
}else{
$out_anagramm .= GetWordAnagramm($value).$span;
}
$i++;
}
}
return $out_anagramm;
}
//Получение аннаграммы отдельного слова
function GetWordAnagramm($anagramm)
{
$array_an = preg_split('//u',$anagramm,-1,PREG_SPLIT_NO_EMPTY);
$j = 0;
$maxLeven = 0;
while ($j < 10)
{
srand((float)microtime() * 1000000);
shuffle($array_an);
$an = "";
$ot = "";
$i = 0;
foreach ( $array_an as $value )
{
$an .= $value." ";
$ot .= mb_substr ($anagramm, $i, 1, 'UTF-8')." ";
$i++;
}
$leven = levenshtein ($an,$ot);
$j++;
if ($leven > $maxLeven)
{
$maxLeven = $leven;
$best_array_an = $array_an;
}
}
$an_div = "";
foreach ( $best_array_an as $value )
{
$an_div .= "<span class=\"annagramm\">".$value."</span> ";
}
return $an_div;
}
Если на страницу вывода добавить CSS-стиль для SPAN-класса annagramm:
span.annagramm {
background-color: #ffffff;
border-style: outset;
border-width: 1px;
border-color: #cccccc;
-webkit-border-radius: 4px;
-moz-border-radius:4px;
border-radius: 4px;
padding: 4px;
padding-left: 6px;
padding-right: 6px;
margin-left: 1px;
margin-right: 1px;
margin-top: 6px;
margin-bottom: 6px;
font-weight: bold;
color: #4f4ba8;
font-size: 11pt;
text-transform: uppercase;
box-shadow: 0 1px 4px rgba(0, 0, 0, .3), -23px 0 20px -23px rgba(0, 0, 0, .8), 23px 0 20px -23px rgba(0, 0, 0, .8), 0 0 40px rgba(0, 0, 0, .1) inset;
}
то получим вот такую «красоту»:

Правильный ответ: Агафон Никитин.
Комментарии (32)
AvioD
10.10.2018 19:15srand((float)microtime() * 1000000);
А чем rand() не подошло то?
$int_anagramm = (int)$anagramm;
Что это? Полагаю, это такая интересная проверка не передали ли int. Но почему не is_numeric/is_integer?
$an = ""; $ot = "";
Вы через неделю забудете что означают эти $an и $ot. А сторонний разработчик вообще не поймет. Стоит давать более понятные названия для переменных. Да и вообще, в их наименовании в php camelCase в моде, но тут уж кому какие фломастеры нравятся…
Но вообще, в идеале, Вам бы PSR'ы почитать. Ну и хороший код — понятный код. В Вашем же случае все ужасно запутано.PolYakov1968 Автор
10.10.2018 20:23-3Хороший код — рабочий код. Кому-то интересно назначение внутренних переменных?
AvioD
10.10.2018 20:57+1Рабочий код может писать, простите, любой дурак. Я сейчас не намекаю на вас, просто это действительно так, учатся самые основы синтаксиса любого языка — максимум сутки. При должном количестве потраченных человеко-часов, он даже будет стабильно работать. Настоящее искусство — написать красивый, структурированный, понятный код, который читается, словно книга. Такой код легко расширять, легко изменять. Ваш код непонятен и запутан, его никто не будет использовать.
PolYakov1968 Автор
10.10.2018 21:17-3Рабочий код может писать, простите, любой дурак.
Очень сомнительное утверждение. Очень!
Я свой код никому не навязываю. Я им делюсь и утверждаю, что он рабочий и выполняет заявленную задачу (для этого и дал ссылку на свой оттестированный большой проект).
Искренне не понимаю, что в выложенном коде можно не понять. :)))
И таки да, код несовершенен, т.к. в нём оценивается качество перемешивания букв анаграммы слов, читаемых традиционно слева-направо. Имеет смысл добавить такую же оценку, если читать справа-налево, чтобы исключить, к примеру анаграмму «бирг» для слова «гриб».
PolYakov1968 Автор
10.10.2018 20:27А чем rand() не подошло то?
Тем и не подошло, что не даёт возможности оценить качество перемешивания букв. :)
vlreshet
10.10.2018 21:00+6Что это делает на хабре? Любой программист способен за 10 минут наваять данный
говнокод.
oxidmod
10.10.2018 21:28+4function GetWordAnagramm(string $word): string { $letters = preg_split('//u', $word, null, PREG_SPLIT_NO_EMPTY); shuffle($letters); return implode('', $letters); }
PolYakov1968 Автор
10.10.2018 21:51-1Ну, да. Только у меня добавлена очень важная на практике проверка качества перемешивания букв посредством функции Левенштейна, дабы отсекать те случайные перемешивания, которые возвращают ту же последовательность букв, что и в исходном слове и даже те, в которых переставлены одна буква (или более — для длинных слов).
Т.е., таких «слабых» анаграмм не получится:
Слон -> лсон
Рубль -> рубьл
malaf
10.10.2018 22:46Ничего не буду говорить про качество кода, задам только несколько вопросов:
1. Что делать если результат функции ещё надо будет отдавать в api, например, для приложения?
2. Что делать верстальщику/фронтенд разработчику если надо будет внести какие-то изменения в html-верстку? Лезть в php-код?
3. У вас получится быстро написать unit-тест для вашего кода, ещё чтобы не надо было его изменять при каждом внесении изменений в функцию?
4. Что делать если надо будет использовать разные кодировки?
5. Насколько хорошо ваш код соответствует принципам SOLID?
6. И последний вопрос, как вы считаете, данная наработка нужна кому-то кроме вас, сможет ли человек легко воспользоваться ею в своём проекте?vlreshet
10.10.2018 23:19У человека подход «мой код работает — значит это хороший код, не знаю я этих всех выдумок ваших, главное что работает». Какой там SOLID и unit-тесты…
PolYakov1968 Автор
10.10.2018 23:25По пунктам.
1. Поправить формат выдачи функции GetWordAnagramm.
2. Нет. Ему нужно понимать DOM.
3. Этот код более 2 лет исправно функционирует в рабочей системе и прошёл тестирование на >10к примеров. Может я и не прав, но зачем что-то менять в том, что исправно работает?
4. Добавить соответствующий параметр к обеим функциям и передавать его в те библиотечные функции, которые того требуют.
5. Не знал, что SOLID-принципы применимы к «плоским» функциям.
6. Вполне возможно, что кому-нибудь пригодится. Встроить в проект, доработать никакого труда не составляет… Ну, а если никому не нужно, то и хрен с ним. :)
Спасибо за отзыв!malaf
10.10.2018 23:581. К сожалению, не всё так просто и этого будет недостаточно, результат функции мы хотим использовать как на веб-странице с использованием html, так и в приложении где нет никакого html. Обе функции содержат работы с html.
2. Чем верстальщику это поможет, он хочет span заменить на div, например? Зачем фронтенд-разработчику писать код, который преобразовывает текущий если можно решить вопрос на этапе формирования этого кода?
3. Благо логика простая и здесь действительно сложно допустить ошибку, но если будете писать более сложную логику с такой же организацией кода, то у вас будут большие сложности с тестированием.
4. И потом не забывать везде использовать этот параметр, иначе можно получить сложно отлавливаемый баг.
5. Верно, данные принципы в первую очередь были сформированы в контексте ООП, но их идеи также могут быть применимы и к процедурному коду. Например, функция должна выполнять одну задачу.
6. Думаю, проще и лучше всё переписать.
Какую-то ценность в данной статье представляет, пожалуй, только информация про применение функции Левенштейна для решения такой задачи, но это не тянет на статью и приведенный код ещё больше портит впечатление о ней.PolYakov1968 Автор
11.10.2018 00:12Отдельно признателен Вам за конструктивный обмен мнениями и критику!
Поскольку функции были вырваны мной из общего кода, то потащили за собой и лишние внутренности. Впредь буду стараться стерилизовать исходники перед публикацией.
AvioD
10.10.2018 23:46Зря стараетесь, тут эффект Даннинга-Крюгера во всей красе. Пока человек сам не прочувствует, он не поймет Ваших доводов.
maximw
11.10.2018 00:03Зачем при выборке и оценке лучшего перемешивания символы склеиваются через пробел?
PolYakov1968 Автор
11.10.2018 01:13Удивительно, но на словах, разряженных пробелами, функция Левенштейна отрабатывает в 2 и более раз быстрее.
maximw
11.10.2018 01:22+1А вы проводили замеры стоит ли эта выгода накладных расходов на серию конкатенаций в цикле?
wertex15
Больше похоже на абракадабру из букв чем на анаграмму.
AvioD
Ну это и есть анаграмма, как я понял.
Не совсем понятно какая практическая или теоретическая ценность данной статьи. Если хочется поделиться своим кодом с сообществом, уместно сделать библиотеку, выложить на гитхаб, в package list. Но уж не писать о решении каждого рядового кейса на хабре, он не резиновый :)
PolYakov1968 Автор
Вы смеётесь?! Из-за двух микроскопических функций делать библиотеку и посылать пользователей в «сады Придонья».
Практическая ценность публикации — готовые функции для решения обозначенной в заголовке статьи задачи. Теоретическая ценность — ноль. Как и у всего программирования как прикладной дисциплины. :)
wertex15
Посмотрите пожалуйста википедию, там есть примеры.
Анаграмма это когда: покраснение превращается в пенсионерка или лепесток в телескоп
А то что выше это это просто перестановка букв.
А функция Левенштейна совсем для другого задумывалась.
PolYakov1968 Автор
Отчасти согласен. Ориентировался на более широкую трактовку семантики этого понятия. Так же как и ряд он-лайн сервисов анаграмм.
Да, спасибо, я в курсе. Но и для моей задачи вполне пригодилась.
Вот теперь сижу и думаю: может я чего нарушил, применив её непрофильно. :)