Вы находитесь на странице: 1из 9

Модульная арифметика

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

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


модульную арифметику, «циклическую» – подобную той, что мы используем при
определении времени: 11 часов плюс 3 часа есть 2 часа. Подобная арифметика
нужна везде, где у нас есть не бесконечное количество чисел, а конечное. Не
обязательно модульная, но об этом далее. Потому что обычная арифметика для
конечного множества чисел не работает – даже суммирование рано или поздно даст
результат за пределами нашего множества. Такая ситуация всегда имеет место при
работе с символами алфавитов, которых всегда конечное число. Типичный пример –
шифр Цезаря. Если вместо каждой буквы мы берём следующую за ней через три по
алфавиту, а – г, б – д, то что мы берём вместо ю? – б.
По большому счёту, при работе с данными в памяти компьютера мы всегда
имеем дело с конечным количеством чисел – просто потому что память конечна.
Иногда «вычисление по модулю» возникает при работе с компьютером само
собой, по ошибке. Например, если добавить к однобайтной (беззнаковой)
переменной значение, которое вместе с тем значением, что у переменной было,
будет большее, чем 255. Такая ситуация называется «переполнением», процессор
компьютера в таком случае устанавливает специальный флаг – флаг переполнения.
Специально вряд ли так будет сделано, но если значение для суммирования
берётся из некого внешнего источника – из файла или от пользователя, такое
возможно.
Обычно мы, конечно, используем достаточно большие размеры переменных,
чтобы для той задачи, которой мы занимаемся (для именно данной задачи!) с ними
можно было работать как с обычными числами, т.е. такие, чтобы самое большое из
потенциально возможных для данной (!) задачи значений не было больше
максимального значения для переменной данного размера.
Однако, переполнение может быть и специально использовано. Например, с
помощью переполнения мы можем реализовать шифр Цезаря, или даже более
сложный (и реально применявшийся в XIX веке) шифр Виженера – для 256-значного
алфавита (однобайтной текстовой кодировки, например) без специального
использования операций по модулю, т.к. операции – сложения, вычитания – будут
происходить по модулю 256 сами собой.

Сегодня мы поговорим о модульной арифметике и других видах арифметики


для конечного набора чисел. Если данную тему вы уже проходили на других
курсах, защиты информации, например, тем проще вам будет решать задачи к этому
занятию.
Поговорим о модульной арифметике для отдельных чисел и для векторов и
матриц. Почему мы так быстро, сразу же, начинаем говорить о векторах? Если
помните, в прошлый раз, при изучении кода Хэмминга, все операции мы делали
побитово, с отдельными битами суммировали, умножали (при умножении на
матрицу). Т.е. мы рассматривали код, например, 10010 не как единое число в
двоичной записи - в данном случае, 18 – а как вектор из отдельных бит.
Несколько слов о векторах. Иногда один и тот же термин в разных областях
используется в разных смыслах, что может приводить к определённой путанице. В
физике вектор – направленная величина. Несложно понять, что двигаться со
скоростью «десять метров в секунду» вверх, вниз, в одну сторону или в
противоположную – очень разные вещи и может привести к совершенно разным
результатам. Описываем мы вектор с помощью координат – набора чисел, проекций
на декартовы оси (или не декартовы, или не проекции, сейчас мы подробнее этой
темы не будем касаться). Но описание придумываем мы, они у нас в голове и на
бумаге, а вектор – направленная величина – существует реально, без всяких
координат. Я могу сказать «десять метров в секунду» и показать рукой, куда
движется тело, и так задать вектор. Если я введу другие оси или другой способ
описания, мои координаты изменятся, причём не как угодно, а так, чтобы описывать
в новой системе ту же величину, но вектор останется тем же. Соответственно,
просто набор чисел – это не вектор. Но т.к. чаще и привычнее всего вектор
описывается набором чисел, в других областях стали называть вектором набор
чисел.
Набор бит в кодовой последовательности не описывает направленную
величину в обычном понимании, тем не менее, такую последовательность называют
вектором, с другой стороны, мы говорили, что кодовые слова можно считать точками
в особенном пространстве с расстоянием, равным расстоянию Хэмминга, так что в
таком понимании кодовые слова описывают вектор, различие размыто.
Но почему мы рассматривали кодовые слова как наборы битов, а не как целые
двоичные числа? Например, в современных компьютерах удобнее работать с
байтами, а не с отдельными битами. Почему бы не складывать, умножать и вычитать
по модулю целые байты? Во-первых, первые коды, исправляющие ошибки и теория
таких кодов появилась, когда современных компьютеров не было, схему,
содержащую отдельный бит вполне можно было взять в руку и каждый такой
элемент стоил определённых денег. Кроме того, коды, исправляющие ошибки,
обрабатываются не только на «больших» компьютерах, но и в простых
контроллерах. Во-вторых, что важнее, с точки зрения теории кодирования
элементарной единицей является всё-таки бит.
Однако, в наиболее популярных современных кодах операции выполняются
действительно не с отдельными битами, а с символами, состоящими из нескольких
бит, прежде всего – с байтами,
Поэтому мы вспомним модульную арифметику по произвольному модулю.
Но сначала несколько слов о двоичной «арифметике по модулю». Помимо
сказанного выше, арифметика по модулю два обладает тем удобством, что
операции в ней очень упрощаются – сложение совпадает с вычитанием и совпадает
с XOR, умножение совпадает с логическим И, делить на 0 нельзя (это касается
любой модульной арифметики), а деление на 1 оставляет делимое тем же,
Ещё раз напомню, что модульная арифметика – та, что мы используем при
вычислении времени. Но как именно мы это делаем? 11 часов плюс 2 часа – 1 час.
Что мы сделали? Мы увидели, что число «перешло через» 12 и отняли 12. Такие
вычисления называются «вычислением по модулю 12». То же происходит при
вычитании – сколько будет на 3 часа раньше часа? 10 часов. Мы всё время остаёмся
внутри выбранного набора чисел. Не переходим ни через модуль, ни в
отрицательные. Кстати, сам модуль в множество не входит, т.е. корректно мы
должны были бы говорить о значении часа от 0 до 11, а 12 – это уже снова нуль, но
традиционно у нас есть и 0, и 12. А сколько будет «через сутки и пять часов,
отсчитывая от восьми часов»? – Час. Здесь мы не просто «перешли через 12», мы
прибавили целые сутки, два раза по 12, и ещё 5. Что мы сделали при вычислении
результата? Мы отняли 12 часов столько раз, сколько нужно, чтобы осталось число,
меньшее 12. То есть, выражаясь языком арифметики, нашли остаток от деления на
модуль «обычного» результата операции. Остаток от деления – это и есть
«отнять делитель столько раз, чтобы осталось число, меньшее, делителя».
Взятие остатка принято обозначать известным вам оператором .
Выражение

означает, что равен остатку от деления на . Что означает, что , где


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

Для суммирования
Для умножения
Для степени

Для вычитания выполняется правило, такое же как для сложения, но с


вычитанием интереснее. Во-первых, обычное вычитание может дать отрицательное
число, что тогда с нахождением остатка? Всё в порядке. Остаток так и находится –
это положительное (неотрицательное) число, такое, что если прибавить его к
(модуль положителен, частное при отрицательном отрицательно), получится
.
Во-вторых, вычитание приводит к вопросу об отрицательных числах. При
обычном вычитании, если вычитаемое больше уменьшаемого, получается
отрицательное число. Но 1 час минус 3 часа равно 10 часов, Да, именно так
отрицательные числа в модульной арифметике и выглядят.
Давайте вспомним определение. Противоположным к данному числом
называется число, при сложении которого с данным получается нуль. А обратным к
данному, кстати, называется число, при умножении которого на данное получается
единица. В обычной арифметике противоположными к привычным положительным
числам были особые «отрицательные числа», среди положительных
противоположных положительным чисел не было. Но в модульной арифметике, как
вы видите из примера выше, числа, противоположные имеющимся в множестве
числам, находятся в самом же множестве – кстати, что нам и нужно, раз мы
работаем только с данным множеством (часов, кодов, букв), никакие операции не
должны выводить из множества.
Противоположные числа легко увидеть, если составить «таблицу сложения»,
составим её, например, для множества чисел по правилам арифметики по
модулю :

Таблица
сложения
a/ 0 1 2 3
b
0 0 1 2 3
1 1 2 3 0
2 2 3 0 1
3 3 0 1 2

В заголовках столбцов и строк указаны слагаемые, в ячейке на пересечении


сумма. По такой таблице легко найти противоположные числа – нужно для каждого
числа используемого множества, т.е. в каждой строке (или столбце) найти нуль и
посмотреть заголовок соответствующего столбца (соответственно, для столбца -
строки), сложение с каким другим числом данного числа даст нуль. Число,
суммирование данного с которым даёт нуль есть по определению противоположное
ему.
Как видите, нуль есть в каждой строке (и каждом столбце) и только один, так что
у каждого числа в модульной арифметике есть противоположенное внутри того же
множества, и только одно.
По этой же таблице можно увидеть и разность – нужно в заголовке строки (или
столбца) выбрать вычитаемое, а в одной из ячеек строки (столбца) – уменьшаемое
(в абзаце выше мы говорили про нуль, здесь - любое), заголовок столбца (для
поиска по столбцу - строки) даст разность.
Почему мы столько говорим о таблицах? Ведь можно просто посчитать по
модулю. Для начала давайте рассмотрим таблицу умножения для модульного
умножения на множестве {0,1,2,3}, аналогичную таблице сложения выше.

Таблица
умножения
a/ 0 1 2 3
b
0 0 0 0 0
1 0 1 2 3
2 0 2 0 2
3 0 3 2 1

По такой таблице тоже можно попробовать найти обратное (как


противоположное, только для умножения) число – то, произведение которого на
данное даст единицу – и произвести деление. Чтобы найти обратное, нужно в ячейке
выбранной строки найти единицу… и так далее.
Но посмотрите на строку для 2, чему равно обратное для двойки? А чему равно
3 разделить на 2? Ни единицы (для поиска обратного), ни тройки в ячейках для
двойки нет. В рамках модульной арифметики по модулю 4 у двойки нет обратного
числа и нельзя три разделить на два, эти значения не определены.
К сожалению, в общем случае (а именно, когда модуль – не простое число) в
модульной арифметике нельзя ввести деление с привычными свойствами и у
некоторых чисел нет обратных.
А нужно ли нам деление? Ну, во-первых, иметь базовую операцию – умножение
– без обратной не очень удобно, а во-вторых, да, деление само по себе тоже нужно.
Помните, мы говорили про генерирующую и проверочную матрицы для кодов? Эти
две матрицы обратны друг другу, одна вычисляется из другой как обратная матрица.
При нахождении обратной матрицы используется деление. Нам повезло – в
арифметике по модулю два (и вообще по модулю – простому числу) деление есть,
по другому модулю может не быть.
Так вот, почему мы говорим о таблицах – нам нужны другие правила
суммирования и умножения для конечных множеств, и фактически, такие правила в
общем случае представляют собой таблицы, с помощью таблиц мы можем задать
любые правила сложения и умножения. Для конечных множеств таблицы – общий
способ задания правил суммирования и умножения. Нам только нужно подобрать
такие таблицы, чтобы выполнялись обычные аксиомы сложения и умножения и у
всех чисел были противоположные и обратные и чтобы для любых двух чисел
существовали не только сумма и произведение, но и разность и частное.
Да, при этом не будут получаться привычные нам результаты суммирования и
умножения, например, 2+3 может стать равным 1 даже если в множестве
присутствует 5. Ну и что? Остановимся на минутку и вспомним цель того, чем мы
сейчас занимаемся. Мы сказали, что в отличие от обычных чисел, количество
которых бесконечно, тех объектов, с которыми мы имеем дело, например, при
составлении кодов, конечное число. Обычная арифметика с конечными
множествами не работает, даже сложение рано или поздно даст результат за
пределами множества. Но нам нужно выполнять арифметические операции с такими
объектами, при составлении кодов и шифров, так что нам нужна арифметика,
операции которой имели бы все обычные свойства, но которая не выводила бы нас
за пределы множества. Однако, при кодировании элементы множества не
выражают какого-то количества, это просто символы, коды. Нет ничего
странного, если бинарная операция – аналог сложения – для кода 2 и кода 3 даст
код 1.
Поэтому нас устраивают правила дающие непривычный для «количественных»
чисел результат. Нас не устраивает, если при вычислениях не будут работать
обычные правила работы с суммами, произведениями, разностями и делениями -
раскрытие скобок, последовательность операций (сначала умножение, затем
суммирование) и т.п. Мы хотим работать по привычным правилам с выражениями, а
какая пара кодов отобразится при суммировании в какой – не так уж важно. Какие
именно правила нам нужны?

Мы хотим работать с конечным множеством чисел, для которых


- введены операции сложения и умножения
- сложение ассоциативно ( ) и коммутативно ( )
- умножение ассоциативно ( ) и коммутативно ( )
- существует нейтральный элемент для сложения (нулевой), такой, что
- существует нейтральный элемент для умножения (единичный), такой, что
- для каждого (!) элемента существует противоположный , такой что
- для каждого (!) элемента , кроме 0, существует обратный , такой что
- работает обычный закон раскрытия скобок (дистрибутивность)

Множество с такими операциями называется полем.


Конечное множество с такими операциями называется полем Галуа.

Для нормальной, удобной работы с кодами и другими подобными конечными


множествами нам нужно ввести для них операции сложения и умножения так, чтобы
сделать из них поля Галуа. Увы, обычная модульная арифметика превращает в поле
Галуа только наборы с числом элементов равным простому числу.

Ну а что с задачами, где нужна именно модульная арифметика, вроде задач о


времени? Что ж, там мы будем использовать модульную арифметику, но обойдёмся
без привычной операции деления.
Таблицы – наиболее общий способ но всё-таки не очень удобный. Да, для 4-х
элементов таблицы относительно небольшие, но мы-то хотим работать, например, с
байтами (256 элементов множества), таблицы будет получаться огромными, кроме
того, как «угадать» с таким количеством ячеек подходящие правила?
Было бы лучше вместо таблиц всё-таки использовать какие-то правила
суммирования и умножения, пусть не совпадающие с операциями по модулю, но
имеющие «правильные» свойства.
На самом деле вы в школе учили другую арифметику, помимо арифметики
обычных чисел, просто на этом отличии обычно не сосредотачиваются. Например,
перемножение векторов не похоже на обычное умножение. Другой пример -
арифметика многочленов. Как вы знаете, ,
т.е. просто независимо суммируются коэффициенты при одинаковых степенях. А вот
с умножением сложнее, используя многочлены из примера выше

как видите, коэффициенты от разных степеней «перемешались». И тем не менее,


это вариант произведения, для него «работают» все правила произведения.
А делить многочлен на многочлен можно, как вы помните, «в столбик» -
деление тоже есть, хотя результат тоже не такой как для обычных чисел, но правила
те же.
Что если бы мы могли поставить каждому числу нашего конечного множества в
соответствие полином и потом проводить вычисления так «как будто» это полиномы
– по правилам для полиномов? А потом опять записывать результат в виде
«обычного» числа.
Вот только произведение полиномов степени, допустим, 3, даёт полином
шестой степени, не похоже на «конечные множества». Это не проблема – для
полиномов существует аналог операций по модулю – нужно каждый раз делить
получившийся полином на специальный полином-«модуль». Для этого используются
полиномы – аналоги простых чисел, «неприводимые полиномы». Такая вот
модульная арифметика на новом уровне – с неприводимым полиномом вместо
модуля-числа.
Минутку, мы снова вернулись к простым числам. Так может, просто
использовать простой модуль и обычную модульную арифметику? Там-то всё
работает, в том числе и деления. Да, но нам при работе с компьютером хотелось бы
использовать не числа вроде 7 или 19, а 32 и 256 – степени двойки. Так вот,
арифметика полиномов позволяет создать арифметику для конечных
множеств, работающую с множествами, число элементов которого равно
степени просто числа.
Кстати, только степени простого числа. Создать нужную нам арифметику для
множества с числом элементов не равным простому числу или степени простого
числа – невозможно, (В примере выше, где нам не удалось найти обратное число
для двойки, число элементов было равно четырём и арифметика полиномов
исправляет ситуацию.)
Для степеней двойки делается это следующим образом. Запишем число в
двоичной записи, а затем запишем полином с коэффициентами (только 0 и 1),
равными цифрам числа, а степень равна степени двойки при соответствующей
цифре. Т.е., например, числу поставим в соответствие
полином . Тогда его сумма с числом, скажем, -
суммирование коэффициентов выполняется по модулю - даст, как при наших
операциях с векторами, , где под понимается побитовое
суммирование по модулю 2, а произведение на число даст
(суммирование коэффициентов по модулю!) и,
используя неприводимый полином получим (делим в столбик,
суммирование/вычитание коэффициентов по модулю)

т.е. остаток от деления (т.е. результат «умножения по модулю» для


рассматриваемых полиномов) равен , что соответствует числу .
Можно построить таблицы умножения по правилу арифметики полиномов и не
производить операции с полиномами каждый раз, просто пользоваться готовой
таблицей. Также можно составить таблицу степеней (по правилам умножения
полиномов для умножения числа само на себя). Такая таблица интересна нам вот
почему. В такой таблице для поля Галуа обязательно будет элемент,
последовательные степени которого равны всем элементам множества. Что это нам
даёт? Допустим, такой элемент - и другое число из множества , а число
. Тогда и мы можем заменить полиномиальное модульное
умножение и (как вы видели, относительно сложное) сложением и (по
модулю) и последующим поиском соответствующего в единственной строке
таблицы степеней – для . И то же с делением.
Например, для множества таким элементом является (что
соответствует полиному ), строка таблицы степеней для неё имеет
вид
Степень
0 1 2 3 4 5 6 7
2 1 2 4 3 6 7 5 1

Как видите, степени начинают повторяться для степени , результат для


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

что, как видно из строки таблицы, равно , как мы и получили прямым


полиномиальным умножением, однако, с помощью таблицы степеней – с помощью
единственной строчки таблицы степеней – гораздо проще.
Обратите внимание, речь не о том, что мы зачем-то вводим в кодирование
полиномы. Сами - иксы в разных степенях – мы не используем. Речь о том, что
мы записываем числа, с которыми мы работаем (кодовые последовательности,
например) в виде полиномов, выполняем операции с ними с помощью арифметики
полиномов, но потом результат снова записываем в виде обычного числа. Потому
что правила арифметики полиномов дают нам ту арифметику, которая нам нужна –
со сложением, вычитанием, умножением и делением, имеющим обычные свойства,
для конечных множеств. Именно такую арифметику полиномов предложил Галуа.
Имея подходящую арифметику для отдельных чисел перейти к арифметике для
векторов и матриц несложно – она будет точно такой же как для векторов и матриц
из обычных чисел, только операции с отдельными элементами будут совершаться
по правилам избранной арифметики для отдельных чисел, например,
полиномиальной.
Мы говорили, что арифметику со всеми нужными свойствами можно построить
только для множеств с числом элементов, равному простому числу и степеням
простого числа, и что для степеней простого числа нужные правила даёт
арифметика полиномов. А можно ли построить другие таблицы сложения и
умножения, не по правилам полиномов, для множеств с числом элементов, равных
степени простого числа? Нет. Никаких других систем правил (с точностью до
переобозначений) построить нельзя.
Напоследок отметим, что модульную арифметику не следует путать с
модулярной, которая представляет собой арифметику в особенной записи чисел –
не привычной нам позиционной, некоторые вычисления в модулярной арифметике
электронные устройства могут осуществлять быстрее, чем в обычной, но некоторые
- наоборот.

Задачи
1. Зашифровать шифром Цезаря по модулю 33 (0 = а, …, 32 = я) со сдвигом 5
вперёд по алфавиту (а → е) слово «эволюция».

2. Используя свойства операций по модулю найти


а) , б) , в)

3. Доказать, что на множестве по модулю сложение совпадает с


вычитанием и , а умножение с . Найти

а) , б) , в)

4. Используя строку таблицы степеней числа в поле Галуа на множестве


найти
а) , б) , в)

5. Считая, что тип char в Си однобайтовый, а модификаторы signed и unsigned


определяют, соответственно, знаковый (со значениями -128 … 127) и беззнаковый
(со значениями 0 … 255) тип,
а) найти, чему будет равно a в результате вычислений
unsigned char a = 250;
a += 10;
б) когда остановится следующий цикл?
signed char a;
for (a = 0; a < 255; a++);
в) найти, чему будет равно a в результате вычислений
char a = 125;
a += 10;
(подсказка: здесь ответ больше относится к языку Си, чем к модульным
вычислениям), данный пункт необязательный.

Вам также может понравиться