Анагра?мма (от греч. ???- — «пере» и ?????? — «буква») — литературный прием, состоящий в перестановке букв или звуков определенного слова (или словосочетания), что в результате дает другое слово или словосочетание
Анаграммы используются для шифрования ответа на вопрос викторины (шарады, кроссворда и т.п.).

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

Работу функций «в боевом», как говорится, режиме можно увидеть здесь.

Для выбора наиболее «перемешанной» анаграммы применяется функция Левенштейна.
Для слов длиной в три буквы и меньше анаграмма не составляется — смысла нет.
Для чисел — то же.
Кодировка — 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;
}

то получим вот такую «красоту»:

image

Правильный ответ: Агафон Никитин.

Комментарии (32)


  1. wertex15
    10.10.2018 18:38

    Больше похоже на абракадабру из букв чем на анаграмму.


    1. AvioD
      10.10.2018 19:05
      +1

      Ну это и есть анаграмма, как я понял.
      Не совсем понятно какая практическая или теоретическая ценность данной статьи. Если хочется поделиться своим кодом с сообществом, уместно сделать библиотеку, выложить на гитхаб, в package list. Но уж не писать о решении каждого рядового кейса на хабре, он не резиновый :)


      1. PolYakov1968 Автор
        10.10.2018 20:34
        -2

        Вы смеётесь?! Из-за двух микроскопических функций делать библиотеку и посылать пользователей в «сады Придонья».
        Практическая ценность публикации — готовые функции для решения обозначенной в заголовке статьи задачи. Теоретическая ценность — ноль. Как и у всего программирования как прикладной дисциплины. :)


      1. wertex15
        10.10.2018 21:55

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


        1. PolYakov1968 Автор
          10.10.2018 22:06

          Анаграмма это когда: покраснение превращается в пенсионерка или лепесток в телескоп
          А то что выше это это просто перестановка букв.

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

          Да, спасибо, я в курсе. Но и для моей задачи вполне пригодилась.
          Вот теперь сижу и думаю: может я чего нарушил, применив её непрофильно. :)


  1. AvioD
    10.10.2018 19:15

    srand((float)microtime() * 1000000);

    А чем rand() не подошло то?
    
    $int_anagramm = (int)$anagramm;
    

    Что это? Полагаю, это такая интересная проверка не передали ли int. Но почему не is_numeric/is_integer?
    $an = "";
    $ot = "";

    Вы через неделю забудете что означают эти $an и $ot. А сторонний разработчик вообще не поймет. Стоит давать более понятные названия для переменных. Да и вообще, в их наименовании в php camelCase в моде, но тут уж кому какие фломастеры нравятся…
    Но вообще, в идеале, Вам бы PSR'ы почитать. Ну и хороший код — понятный код. В Вашем же случае все ужасно запутано.


    1. skymal4ik
      10.10.2018 19:36

      (int) используется для приведения типа в int. Соответственно есть и другие операторы для других типов.


      1. AvioD
        10.10.2018 21:07

        Да, я это знаю. Но зачем приводить строку с текстом к int?


        1. PolYakov1968 Автор
          10.10.2018 21:29
          -2

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


    1. PolYakov1968 Автор
      10.10.2018 20:20

      Именно так. Анаграмма для чисел — нонсенс.


    1. PolYakov1968 Автор
      10.10.2018 20:23
      -3

      Хороший код — рабочий код. Кому-то интересно назначение внутренних переменных?


      1. AvioD
        10.10.2018 20:57
        +1

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


        1. PolYakov1968 Автор
          10.10.2018 21:17
          -3

          Рабочий код может писать, простите, любой дурак.

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

          И таки да, код несовершенен, т.к. в нём оценивается качество перемешивания букв анаграммы слов, читаемых традиционно слева-направо. Имеет смысл добавить такую же оценку, если читать справа-налево, чтобы исключить, к примеру анаграмму «бирг» для слова «гриб».


          1. AvioD
            10.10.2018 21:21

            Как скажете. Продолжите заниматься программированием — придете к моим словам.


            1. PolYakov1968 Автор
              10.10.2018 21:30

              И вам больших свершений!


    1. PolYakov1968 Автор
      10.10.2018 20:27

      А чем rand() не подошло то?

      Тем и не подошло, что не даёт возможности оценить качество перемешивания букв. :)


    1. horror_x
      10.10.2018 22:31

      Т.е. вёрстка прямо в этих функциях вас не смутила? :)


      1. PolYakov1968 Автор
        10.10.2018 22:48
        -3

        Меня? — Нет.


  1. vlreshet
    10.10.2018 21:00
    +6

    Что это делает на хабре? Любой программист способен за 10 минут наваять данный говнокод.


    1. PolYakov1968 Автор
      10.10.2018 21:24
      -6

      Благодарю за чудесный говнокомментарий!


  1. oxidmod
    10.10.2018 21:28
    +4

    function GetWordAnagramm(string $word): string
    {
        $letters = preg_split('//u', $word, null, PREG_SPLIT_NO_EMPTY);
        shuffle($letters);
        
        return implode('', $letters);
    } 


    1. porn
      10.10.2018 21:44
      +1

      Пилите статью.


    1. PolYakov1968 Автор
      10.10.2018 21:51
      -1

      Ну, да. Только у меня добавлена очень важная на практике проверка качества перемешивания букв посредством функции Левенштейна, дабы отсекать те случайные перемешивания, которые возвращают ту же последовательность букв, что и в исходном слове и даже те, в которых переставлены одна буква (или более — для длинных слов).
      Т.е., таких «слабых» анаграмм не получится:
      Слон -> лсон
      Рубль -> рубьл


  1. malaf
    10.10.2018 22:46

    Ничего не буду говорить про качество кода, задам только несколько вопросов:

    1. Что делать если результат функции ещё надо будет отдавать в api, например, для приложения?
    2. Что делать верстальщику/фронтенд разработчику если надо будет внести какие-то изменения в html-верстку? Лезть в php-код?
    3. У вас получится быстро написать unit-тест для вашего кода, ещё чтобы не надо было его изменять при каждом внесении изменений в функцию?
    4. Что делать если надо будет использовать разные кодировки?
    5. Насколько хорошо ваш код соответствует принципам SOLID?
    6. И последний вопрос, как вы считаете, данная наработка нужна кому-то кроме вас, сможет ли человек легко воспользоваться ею в своём проекте?


    1. vlreshet
      10.10.2018 23:19

      У человека подход «мой код работает — значит это хороший код, не знаю я этих всех выдумок ваших, главное что работает». Какой там SOLID и unit-тесты…


    1. PolYakov1968 Автор
      10.10.2018 23:25

      По пунктам.
      1. Поправить формат выдачи функции GetWordAnagramm.
      2. Нет. Ему нужно понимать DOM.
      3. Этот код более 2 лет исправно функционирует в рабочей системе и прошёл тестирование на >10к примеров. Может я и не прав, но зачем что-то менять в том, что исправно работает?
      4. Добавить соответствующий параметр к обеим функциям и передавать его в те библиотечные функции, которые того требуют.
      5. Не знал, что SOLID-принципы применимы к «плоским» функциям.
      6. Вполне возможно, что кому-нибудь пригодится. Встроить в проект, доработать никакого труда не составляет… Ну, а если никому не нужно, то и хрен с ним. :)

      Спасибо за отзыв!


      1. malaf
        10.10.2018 23:58

        1. К сожалению, не всё так просто и этого будет недостаточно, результат функции мы хотим использовать как на веб-странице с использованием html, так и в приложении где нет никакого html. Обе функции содержат работы с html.
        2. Чем верстальщику это поможет, он хочет span заменить на div, например? Зачем фронтенд-разработчику писать код, который преобразовывает текущий если можно решить вопрос на этапе формирования этого кода?
        3. Благо логика простая и здесь действительно сложно допустить ошибку, но если будете писать более сложную логику с такой же организацией кода, то у вас будут большие сложности с тестированием.
        4. И потом не забывать везде использовать этот параметр, иначе можно получить сложно отлавливаемый баг.
        5. Верно, данные принципы в первую очередь были сформированы в контексте ООП, но их идеи также могут быть применимы и к процедурному коду. Например, функция должна выполнять одну задачу.
        6. Думаю, проще и лучше всё переписать.

        Какую-то ценность в данной статье представляет, пожалуй, только информация про применение функции Левенштейна для решения такой задачи, но это не тянет на статью и приведенный код ещё больше портит впечатление о ней.


        1. PolYakov1968 Автор
          11.10.2018 00:12

          Отдельно признателен Вам за конструктивный обмен мнениями и критику!
          Поскольку функции были вырваны мной из общего кода, то потащили за собой и лишние внутренности. Впредь буду стараться стерилизовать исходники перед публикацией.


    1. AvioD
      10.10.2018 23:46

      Зря стараетесь, тут эффект Даннинга-Крюгера во всей красе. Пока человек сам не прочувствует, он не поймет Ваших доводов.


  1. maximw
    11.10.2018 00:03

    Зачем при выборке и оценке лучшего перемешивания символы склеиваются через пробел?


    1. PolYakov1968 Автор
      11.10.2018 01:13

      Удивительно, но на словах, разряженных пробелами, функция Левенштейна отрабатывает в 2 и более раз быстрее.


      1. maximw
        11.10.2018 01:22
        +1

        А вы проводили замеры стоит ли эта выгода накладных расходов на серию конкатенаций в цикле?