КДПВ Иногда возникает задача защитить строку-идентификатор от случайных ошибок, сделанных человеком. Например, номер платёжной карты. Для этого к строке добавляется вычисленная специальным образом контрольная цифра, и когда человек вводит этот номер, можно сделать первичную проверку на ошибки без обращения к базе данных. Самый простой вариант — добавить остаток от деления суммы всех цифр на 10, в таком случае искажение любой одной цифры (в том числе контрольной) будет легко обнаружить, и такая строка не пройдёт проверку. Но некоторые другие ошибки такая контрольная сумма пропустит, например, перестановку двух цифр местами, а это тоже довольно распространённая ошибка.

Для защиты номеров банковских карт выбран несколько более сложный алгоритм, алгоритм Луна. В нём добавлена одна модификация: цифры, стоящие на чётных местах, перед суммированием умножаются на 2 (а если получается больше девяти, то вычитается 9). Это позволяет словить помимо искажения любой цифры большинство (хотя и не все) перестановок соседних цифр.

Существуют ли алгоритмы, позволяющие обнаружить любые перестановки соседних цифр (помимо искажения любой одиночной цифры)? Да, существуют, хотя они почему-то не очень распространены. Это алгоритм Верхуффа, основанный на диэдральных группах, и алгоритм Дамма, который основан на квазигруппах Дамма. Звучит пугающе, но на самом деле там нет ничего сложного (для тех, кто знаком с понятием «группа»).

Якоб Верхуфф (Jacobus Verhoeff), нидерландский математик и скульптор (на фотографии одна из его скульптур), предложил алгоритм на основании диэдральных групп. Диэдральная группа — это группа симметрий правильного N-угольника, включающая как вращения, так и осевые симметрии. Такая группа не является коммутативной: если правильный многоугольник с перенумерованными вершинами сначала симметрично отобразить, а потом повернуть, то в большинстве случаев получится не то же самое, что если сначала повернуть, а потом отобразить. А поэтому при последовательном «умножении» цифр исходной строки, используя операцию диэдральной группы, т.е. последовательно применяя операции поворота и симметричного отображения над правильным N-угольником, мы получим защиту от большинства перестановок. От большинства, но всё-таки опять не от всех. Верхуфф предложил усовершенствовать этот алгоритм, и перед умножением каждую цифру заменять на другую по специальной таблице, в зависимости от места цифры в строке. Таблица получена из одной перестановки путём последовательного её применения к предыдущему результату, и через 8 таких применений мы приходим к исходному порядку, поэтому можно заранее построить таблицу 8x10 и брать значение оттуда. Некоторые из таких перестановок позволяют определять все 100% возможных ошибок в порядке соседних цифр исходной строки, то есть это является полным и корректным решением задачи нахождения контрольной цифры, защищающей от этих двух типов ошибок. Судя по всему, удачную перестановку Верхуфф нашёл методом случайного подбора, их таких существует довольно много.

Вопрос о том, существуют ли группы, позволяющие находить любые перестановки соседних цифр без дополнительных модификаций, долгое время оставался открытым. И уже в XXI веке, в 2004 году немецкий математик Michael Damm доказал существование таких групп, они называются слабо полностью антисимметричными квазигруппами (weakly totally anti-symmetric quazigroup). Я не полностью разобрался в способе их построения, желающие могут попробовать это сделать самостоятельно по его публикации. И хотя при беглом просмотре она не выглядит такой уж сложной (даже странно, что вопрос оставался открытым так долго), для практической реализации есть более простой способ: воспользоваться готовыми таблицами.

Теперь перейдём к следующему и основному вопросу: что делать, если нужно защитить не строку десятичных цифр, а строку произвольных символов, например, 16-ричных или base64 или base58? То есть, решить задачу не для частного случая десятичной системы счисления, а в общем виде.

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

Гугление ничего не дало, кроме отдельных попыток и применения «достаточно хороших» решений (т.е. обнаруживающих почти все перестановки) для N=64.

Пожалуй, не буду описывать все испробованные способы поиска нужной перестановки, которые не дали результата. Я пытался решить частную задачу: найти перестановку для base64 и base58, дающую защиту от изменения порядка соседних цифр. Скажу лишь, что попытки найти такую перестановку методом случайного или последовательного перебора с разными вариантами оптимизации ни к чему не привели. Но в итоге я нашёл общее решение для любого чётного n.

Перестановка такая:

0, N-1 .. N/2+1, 1 .. N/2

Например, для N = 10 это будет:

0, 9, 8, 7, 6, 1, 2, 3, 4, 5

Эта перестановка обладает ещё одним замечательным свойством: у неё период всегда (для N >= 8) равен 12, что позволяет заранее построить таблицу 12xN и брать цифру оттуда.

Вот здесь пример реализации предлагаемого расширения алгоритма Верхуффа для base58 (строго говоря, это уже не алгоритм Верхуффа, а его обобщение). Не полноценная либа, а просто утилитка, так сказать, proof of concept.

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

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


  1. AntonSazonov
    09.01.2019 01:23

    А в реализации намеренно пропущены символы "0IOl" (ноль, ай, оу, эль) в массиве alphabet, или это алгоритм такой? Из статьи не удалось понять.


    1. tbl
      09.01.2019 01:51

      По сравнению с Base64 в Base58 в том числе отсутствуют символы, которые могут быть неоднозначно восприняты человеком


      1. AntonSazonov
        09.01.2019 02:05

        Как раз это я и пропустил. Благодарю за подсказку.


      1. a-tk
        09.01.2019 10:59

        А для чего такие алфавиты нужны? Архивы передавать голосом по телефону?


        1. tbl
          09.01.2019 12:04

          Алфавит используется для биткоин-адресов, чтобы можно было без ошибок перепечатать с картинки


        1. gul_kiev Автор
          09.01.2019 16:06

          Помимо bitcoin-адресов это может быть произвольная строка-идентификатор, будь то аккаунт в соц-сети, ИНН, номер паспорта, идентификатор брони в отеле, авиабилета и т.п. Иногда передаются и голосом по телефону, да. Как base58 он получается короче, а потому удобнее, чем набор десятичных цифр.
          Кроме того, такие идентификаторы удобно передавать баркодом (например, печатать на посадочном талоне).


  1. amarao
    09.01.2019 12:06

    Буквально только что на Computerfile был рассказ про isbn и контрольную сумму в нём. Там используется модульная арифметрика по основанию 11. Они обещают способность обнаружить любые перестановки и искажение любой одной цифры.

    www.youtube.com/watch?v=bqtE6Q79PPs


    1. tbl
      09.01.2019 12:28

      Почти все алгоритмы для forward error correction работают над конечными числовыми полями, и очень большой класс из них решают 2 задачи: обнаружение ошибки, исправление ошибки. Скорее всего схема, используемая в isbn — частный случай уже существующего кодирования.


      1. Fragster
        09.01.2019 18:19

        ISBN он же EAN-13 он же GS1-13 использует алгоритм модуло10, он же алгоритм Луна, только умножение происходит не на два, а на три. Хотя ранее (года примерно до 2007) был иной алгоритм, с модулем 11 и возможным контрольным символом X. Собственно, переход к EAN-13 и состоял в добавлении префикса 978 и перерасчете контрольной цифры.


    1. gul_kiev Автор
      10.01.2019 07:42

      При использовании арифметики по модулю 11 нужно как-то записывать остаток 10. Обычно его записывают как 0, и в результате получается защита от большинства (больше 90%) ошибок, но не от всех.

      В Википедии тоже обещают, что код ОКПО, основанный на том же принципе (умножение цифр на их номер в строке, и как контрольную цифру берём остаток от деления на 11 суммы), даёт 100% защиту и от искажения одиночной цифры, и от любой перестановки соседних цифр, но на самом деле это не так, там возможны коллизии, и даже одиночная ошибка может быть пропущена.

      В ОКПО сделан такой финт:
      Если получается остаток, равный 10, то для обеспечения одноразрядного контрольного числа необходимо провести повторный расчет, применяя вторую последовательность весов, сдвинутую на два разряда влево (3, 4, 5,…). Если в случае повторного расчета остаток от деления вновь сохраняется равным 10, то значение контрольного числа проставляется равным «0».

      Но он не уменьшает количество определяемых ошибок. Например, для числа 2256261 получается контрольная цифра 2 — такая же, как и для чисел 5256261, 2956261, 2266261, 2254261, 2256761, 2256211 и 2256263, которые отличаются от него всего одной цифрой.


      1. Zenitchik
        10.01.2019 14:57
        +1

        При использовании арифметики по модулю 11 нужно как-то записывать остаток 10.

        В ISBN его записывают буквой X.


        1. gul_kiev Автор
          10.01.2019 16:07

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


  1. domix32
    09.01.2019 15:34
    +1

    А где же тесты?


  1. VaalKIA
    10.01.2019 09:08

    Такая группа не является коммутативной: если правильный многоугольник с перенумерованными вершинами сначала симметрично отобразить, а потом повернуть, то в большинстве случаев получится не то же самое, что если сначала повернуть, а потом отобразить.

    А нарисовать 4 картинки — не, зато КДПВ — нате!


    1. gul_kiev Автор
      10.01.2019 16:22

      Я не ставил целью объяснять, что такое диэдральные группы и вообще группы. Цель поста — показать полученное мной решение для задачи, которая, похоже, до сих пор была нерешённой. Остальное — лишь необходимое введение в контекст. О том, что такое системы счисления и какие они бывают, где применяется base58, что такое группа и т.д. я не писал, иначе это было бы слишком долго, да и незачем — об этом есть другие статьи.
      Про диэдральные группы можно подробнее почитать хоть в Википедии, там с картинками.

      Лично мне нравится, когда помимо сухого материала что-то говорится об авторе, когда он «оживляется», и из названия теоремы превращается в живого человека, поэтому и показал работу Верхуффа как скульптора. Если вам это не по душе — извините.