# теория · шаг 1 из 9
Арифметика остатков: что можно делать по модулю и чего нельзя
Коротко
Остаток суммы и произведения можно считать по остаткам слагаемых и множителей. Поэтому, если нужен только
остаток результата, остаток берут после каждого действия, и числа не растут: остаток 1000! от деления на
1 000 003 считается за 3001 шаг, хотя сам 1000! - число из 2568 цифр. Складывать, вычитать и умножать по
модулю можно свободно. Делить нельзя: по модулю 6 произведение 2 · 3 равно нулю, хотя ни один множитель
не ноль, и сократить двойку в равенстве 2 · 3 ≡ 2 · 0 значит получить неправду. Когда деление всё-таки
возможно - тема следующих уроков.
Если ты объяснишь, почему остаток произведения считается по остаткам множителей, откуда берётся признак делимости на 9 и почему по модулю 6 нельзя сокращать на 2, - листай до «Теперь сам».
Как думаешь, нужно ли считать 2568 цифр
1000! - произведение всех чисел от 1 до 1000 - занимает 2568 десятичных цифр. Прошлая глава показала,
сколько стоит такое число: список разрядов и проход по нему на каждом умножении.
Теперь пусть само число не нужно, а нужен его остаток от деления на 1 000 003. Прежде чем читать дальше, ответь себе: обязательно ли сначала получить все 2568 цифр, а потом делить? Или остаток можно узнать, ни разу не увидев числа длиннее десяти цифр?
На что это похоже
Представь циферблат. Сейчас десять часов, через пять часов будет три - стрелка прошла через двенадцать и начала заново. Через сто часов будет два: стрелка сделала восемь полных оборотов и ещё четыре часа, и обороты для ответа не важны.
Циферблат не помнит, сколько раз стрелка обошла круг. Он хранит только положение на круге - остаток. И чтобы узнать, где стрелка окажется, складывать можно прямо положения: десять плюс четыре - четырнадцать, это два.
Механизм: почему остатков достаточно
Любое число раскладывается на целое число оборотов и остаток: a = q · m + r, где 0 ≤ r < m. Число m
называют модулем; запись a mod m без скобок - действие: остаток от деления a на m. Два числа сравнимы по модулю m, если остатки у них одинаковые; пишут
a ≡ b (mod m). То же самое другими словами: разность a − b делится на m. 17 ≡ 2 (mod 5), потому
что 17 − 2 = 15.
Возьмём два числа: a = q₁ · m + r₁ и b = q₂ · m + r₂.
a + b = (q₁ + q₂) · m + (r₁ + r₂)
a · b = (q₁q₂m + q₁r₂ + q₂r₁) · m + r₁ · r₂
└────── всё, что делится на m ──────┘ └ то, что решает остаток
В обеих строках первое слагаемое делится на m и на остаток не влияет. Значит остаток суммы - это
остаток от r₁ + r₂, а остаток произведения - остаток от r₁ · r₂. Обороты можно выбросить до действия,
а не после.
Это и есть причина, по которой в формулах раздела стоит именно остаток, а не «обрежем число до последних цифр как-нибудь»: остаток - единственный способ укоротить число, который переживает сложение и умножение.
С делением так не выходит. Вот таблицы умножения ненулевых остатков по модулям 6 и 7:
mod 6 1 2 3 4 5 mod 7 1 2 3 4 5 6
1 1 2 3 4 5 1 1 2 3 4 5 6
2 2 4 0 2 4 2 2 4 6 1 3 5
3 3 0 3 0 3 3 3 6 2 5 1 4
4 4 2 0 4 2 4 4 1 5 2 6 3
5 5 4 3 2 1 5 5 3 1 6 4 2
6 6 5 4 3 2 1
По модулю 7 в каждой строке все остатки встречаются по одному разу. По модулю 6 в строках 2, 3 и 4 есть
нули и повторы: 2 · 3 = 0, 2 · 1 = 2 · 4. Спросить «на что умножили двойку, чтобы получить 2» нельзя -
ответов два. Разделить на 3 единицу нельзя вовсе: в строке тройки единицы нет.
Правило
Если a ≡ a' и b ≡ b' по модулю m, то a + b ≡ a' + b', a − b ≡ a' − b' и a · b ≡ a' · b'.
Поэтому в выражении из сложений, вычитаний и умножений остаток можно брать после любого действия, и
ответ не изменится. Делить и сокращать по модулю в общем случае нельзя: из a · c ≡ b · c не следует
a ≡ b. Остатки не упорядочены: из a mod m < b mod m ничего не следует про a и b.
Разбор: остаток факториала без факториала
r = 1
i = 2
while i <= 1000:
r = r * i % 1000003
i = i + 1
print(r)
Умножение и остаток в Python равноправны и выполняются слева направо: сначала r * i, потом остаток.
Печатает 864722, шагов 3001 - по три на виток. Перед каждым умножением r меньше 1 000 003, множитель
не больше 1000, произведение меньше 1,1 · 10⁹. Ответ на вопрос из начала: ни одного длинного числа не
понадобилось.
Сравни с прошлой главой: там само число пришлось бы держать списком разрядов и проходить его целиком на каждом из тысячи умножений. Здесь цена линейна, потому что длина чисел заперта модулем.
Разбор: признак делимости на 9 и чего он стоит
Школьное правило: число делится на 9, если сумма его цифр делится на 9. Теперь видно, откуда оно.
10 ≡ 1 (mod 9), значит и 100 = 10 · 10 ≡ 1, и любая степень десяти сравнима с единицей. Число
4 · 100 + 2 · 10 + 8 сравнимо с 4 + 2 + 8. Сумма цифр - это и есть число, посчитанное по модулю 9.
Отсюда старая бухгалтерская проверка умножения. 123 456 · 654 321 = 80 779 853 376. Суммы цифр
множителей: 21 и 21, по модулю 9 это 3 и 3, произведение 9, то есть 0. Сумма цифр результата: 63, по
модулю 9 это 0. Сошлось. Если бы в результате была ошибка в одной цифре, остаток почти наверняка
получился бы другим.
«Почти» - и есть граница приёма. Проверка по модулю 9 не видит перестановку цифр: сумма та же. И не видит ошибку ровно на 9. Из всех неверных ответов она пропускает примерно каждый девятый. Совпадение остатков - не доказательство равенства, а несовпадение - доказательство неравенства; тот же принцип был у хешей в Рабине-Карпе.
Теперь про сокращение. 2 · 3 ≡ 2 · 0 (mod 6): слева 6, справа 0, остатки равны. Сократи двойку -
получится 3 ≡ 0 (mod 6), неправда. Сокращение на c - это деление на c, а таблица выше показала,
что по модулю 6 на двойку делить нельзя. По модулю 7 то же сокращение законно при любом ненулевом c.
Что ломается без этого
Первое: остаток взят поздно. r = r * i в цикле и % m в самом конце - числа успевают вырасти. В
Python это тихо превращает линейную программу в квадратичную, в C - в переполнение, на тренажёре - в
ошибку точности.
Второе: произведение двух остатков не помещается в тип. Остатки меньше m, произведение - до m².
При m около миллиарда это 10¹⁸: в 64 бита входит, в 53 бита точности тренажёра и JavaScript - нет.
Правило: модуль не больше корня из предела типа. На тренажёре это 94 906 265.
Третье: деление посреди формулы. Число сочетаний - это n! / (k! · (n − k)!). Посчитать три
факториала по модулю и поделить один остаток на другие нельзя: получится другое число или дробь.
Четвёртое: отрицательный остаток. В Python -7 % 5 равно 3, в C, Java и JavaScript - -2.
Формулы с вычитанием при переносе на другой язык надо завершать (x % m + m) % m.
Пятое: сравнение остатков. a % m < b % m не говорит, что a < b. По остаткам нельзя искать
наибольшее и нельзя сортировать.
Зачем это в работе
Индекс в хеш-таблице - остаток хеша от деления на число ячеек. Выбор сервера для пользователя -
user_id % N. Следующая позиция в кольцевом буфере - (i + 1) % n. Всё это та же арифметика на круге.
Контрольные цифры. Последняя цифра штрихкода подобрана так, чтобы сумма всех цифр, в которой каждая вторая цифра взята с множителем 3, делилась на 10. Номер банковского счёта в формате IBAN проверяется остатком от деления на 97: правильный номер даёт единицу. Касса и банк ловят опечатку до того, как пойдут искать товар или счёт.
Счёт вариантов. Число способов что-то сделать быстро перерастает любой тип, и в задачах просят ответ по модулю - обычно большого простого числа. Простого - затем, что по простому модулю можно делить, и про это два следующих урока.
Криптография. Все действия RSA и обмена ключами - умножения по модулю длиной в тысячи бит. Модуль держит длину чисел постоянной, и поэтому счёт «сколько умножений» там честен.
Теперь сам
- Найди
17 · 23 mod 5, не перемножая 17 и 23. - Делится ли 123 456 789 на 9? А на 3?
- По модулю 6 найди
x, отличный от 1, для которого4 · x ≡ 4. Что это говорит о сокращении на 4? - Чему равно
-7 % 5в Python и в C?
Ответ. 1. Остатки множителей: 2 и 3. 2 · 3 = 6, остаток 1. Проверка: 17 · 23 = 391 = 78 · 5 + 1.
-
Сумма цифр 45, она делится на 9 - значит число делится на 9, а с ним и на 3.
-
x = 4:4 · 4 = 16 = 2 · 6 + 4. Из4 · 4 ≡ 4 · 1сокращением четвёрки получилось бы4 ≡ 1- неправда. По модулю 6 на 4 сокращать нельзя. -
В Python получится 3: частное округляется вниз,
-7 = -2 · 5 + 3. В C получится-2: там частное округляется к нулю,-7 = -1 · 5 − 2.
Главное
Остаток - единственный способ укоротить число, который не ломает сложение и умножение: обороты можно выбрасывать после каждого действия, и результат по модулю будет тем же. Это снимает главную беду прошлой главы - рост длины. Плата - деление: по модулю оно есть не всегда, и вопрос «когда на число можно делить» оказывается вопросом об общих делителях числа и модуля.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий