Арифметика
Операции с дробями
Цели урока
- Привести любые дроби к общему знаменателю через НОК
- Складывать и вычитать дроби с разными знаменателями
- Умножать дроби с предварительным сокращением
- Делить дроби через переворот и умножение
- Видеть связь школьной арифметики с PyTorch, A/B-тестами и Брезенхэмом
Предварительные знания
- Понимание дроби как части целого (введение в дроби)
- Знание таблицы умножения
- Понятие НОК и НОД на интуитивном уровне
Каждая итерация обучения GPT-style модели в смешанной точности (FP16 forward, FP32 master weights) - это сложение тысяч градиентов-дробей с общим знаменателем размера батча. NVIDIA Apex (2018) и встроенный torch.cuda.amp ускорили обучение в 2-3 раза именно за счёт грамотной арифметики дробей с разной точностью.
- **PyTorch gradient accumulation** - суммирование микро-батчей как дробей с общим знаменателем
- **Bresenham line algorithm (1962)** - рисование линий на дисплее через целочисленную арифметику дробей dx/dy
- **Audio sample-rate conversion 44.1 кГц -> 48 кГц** - ratio 147/160, ресемплинг через НОК частот
- **A/B-тесты в Booking, Airbnb, GrowthBook** - lift = (variant/control) - 1, деление двух дробей-конверсий
- **Cron и Kubernetes scheduler** - hyperperiod задач = НОК их периодов, точка синхронизации
Папирус Ринда и египетские дроби
Древние египтяне записывали все дроби как сумму единичных: 2/5 = 1/3 + 1/15. Папирус Ринда содержит 84 задачи на такие разложения. Эта идея вернулась в XX веке как алгоритм Фибоначчи-Сильвестра и применяется в современной криптографии при работе с непрерывными дробями для атак на RSA с малыми экспонентами.
Общий знаменатель
**cron-задачи на Linux-сервере, запущенные с периодами 1/3 и 1/4 часа, синхронизируются ровно раз в час - в момент НОК(3, 4) = 12 интервалов.** Тот же приём планировщики в Kubernetes и Airflow используют для расчёта rendezvous-окон. Сложение дробей и поиск общего расписания - одна и та же операция: чтобы говорить на одном языке, нужен **одинаковый знаменатель**. Третьи и четвёртые - это разные единицы, как метры и футы.
**Общий знаменатель** - число, делящееся на все знаменатели. Оптимальный выбор - **НОК** (наименьшее общее кратное), он же НОК. В планировщиках задач это называют hyperperiod: интервал, через который вся система возвращается в исходное расписание. Для 1/3 и 1/4: НОК(3, 4) = 12.
Ключевая идея: умножение числителя и знаменателя на одно и то же число не меняет значения. Это как ресемплинг массива - количество отсчётов растёт, а сигнал тот же. Именно так работает аудио sample-rate conversion с 44.1 кГц до 48 кГц: 44100/48000 = 147/160, и драйвер ищет НОК между двумя частотами, чтобы выстроить общую сетку семплов.
При приведении к общему знаменателю достаточно поменять только знаменатель
Числитель и знаменатель умножаются на один и тот же множитель
Дробь - это деление. 2/3 ≠ 2/15: одно равно 0.666…, другое 0.133…. Эквивалентность сохраняется только при умножении обеих частей на одинаковое число.
Чему равна дробь 2/3, приведённая к знаменателю 15?
Сложение и вычитание
Когда знаменатели одинаковые, сложение и вычитание сводятся к работе с числителями. Этот же приём лежит в основе **gradient accumulation в PyTorch**: при обучении больших моделей градиенты считаются по микро-батчам и суммируются как дроби с общим знаменателем (общим числом примеров) перед шагом оптимизатора.
**Kahan summation** - алгоритм компенсированного суммирования в численных библиотеках NumPy и BLAS. Когда складывается миллион дробных чисел разного порядка, ошибка округления накапливается. Kahan хранит "остаток" отдельно - буквально дробную часть, потерянную при сложении, - и добавляет её в следующий шаг. Та же идея, что и приведение к общему знаменателю: ничего не должно потеряться.
**Алгоритм сложения и вычитания:** 1. Привести к общему знаменателю. 2. Сложить или вычесть числители. 3. Знаменатель оставить. 4. Сократить, если возможно.
После сложения результат проверяется на сократимость: 4/8 сводится к 1/2. В производственном коде numerical libraries это шаг нормализации - тот же, что выполняет Python `Fraction` из модуля `fractions` через НОД числителя и знаменателя.
Чему равно 3/4 + 1/6?
Умножение дробей
**Maximum likelihood estimation в каждой ML-библиотеке начинается с произведения вероятностей: $P(x_1)\cdot P(x_2)\cdot \ldots \cdot P(x_n)$.** Каждая P - дробь от 0 до 1, и правило их умножения - то же самое правило, что для дробей в школе: числитель на числитель, знаменатель на знаменатель. Общий знаменатель не нужен - именно поэтому умножение проще сложения.
**Log-likelihood в практике:** прямое перемножение тысяч вероятностей даёт underflow в float64 уже на 700-800 множителях. Поэтому в TensorFlow и PyTorch вместо $\prod P_i$ считают $\sum \log P_i$ - произведение дробей превращается в сумму логарифмов. Структура та же, что и в школьной арифметике, только в другом базисе.
**Трюк: сокращение крест-накрест** Перед умножением можно сократить любой числитель с любым знаменателем: 6/15 × 5/8 = (6/2)/(15/5) × (5/5)/(8/2) = 3/3 × 1/4 = 1 × 1/4 = 1/4.
Умножение дроби на целое число: целое представляется как дробь со знаменателем 1. 3 × 2/5 = 3/1 × 2/5 = 6/5. Тот же приём в **алгоритме Брезенхэма для рисования линий**: вместо float-арифметики компьютер хранит ошибку как дробь с целым числителем и знаменателем (dx, dy), и умножения превращаются в целочисленные сложения.
Перед умножением дроби нужно привести к общему знаменателю
Общий знаменатель нужен только для сложения и вычитания
Умножение - это "часть от части", оно не требует одинаковых единиц. (a/b)(c/d) = (ac)/(bd) по определению. Принудительное приведение к общему знаменателю даст тот же ответ, но удвоит работу.
Чему равно 3/4 × 2/9?
Деление дробей
**A/B-тесты в продакшне сравнивают конверсии: 124/10000 против 137/10000. Чтобы получить relative lift, одна дробь делится на другую.** Деление дробей сводится к умножению по правилу: **делить на дробь = умножать на перевёрнутую** (обратную). Тот же приём в learning-rate scheduler: чтобы пересчитать шаг при изменении batch size c B1 на B2, lr делят на (B1/B2), что равносильно умножению на (B2/B1).
**Rate normalisation в продакшне:** во всех A/B-фреймворках (Optimizely, GrowthBook, in-house инструменты Booking.com) lift считается как (variant_rate / control_rate) - 1. Это деление двух дробей. Если variant даёт 137/10000, а control 124/10000, то lift = (137/124) - 1 ≈ 10.5%. Цена ошибки в этой формуле - неправильный продуктовый rollout.
**Мнемоника для деления:** "Первую оставить, знак поменять, вторую перевернуть". a/b / c/d = a/b × d/c.
Деление на дробь меньше единицы даёт результат **больше** делимого. Сколько половинок в целом? Две: 1 / 1/2 = 2. Тот же эффект в десятичных дробях: деление на 0.5 равносильно умножению на 2. Десятичные и обыкновенные - две формы одной и той же арифметики.
При делении дробей делим числители и знаменатели отдельно
Деление дроби равно умножению на обратную дробь
a/b / c/d не равно (a/c)/(b/d). Правильная формула: a/b / c/d = a/b × d/c = (a×d)/(b×c). Например 1/2 / 1/4 = 1/2 × 4/1 = 2.
Чему равно 5/6 / 2/3?
Ключевые идеи
- Сложение и вычитание: общий знаменатель через НОК, потом - арифметика числителей
- Умножение: числитель на числитель, знаменатель на знаменатель, общий знаменатель не нужен
- Деление: переворот второй дроби и умножение
- Сокращение результата до несократимой формы - стандартный финальный шаг
- Эти же правила лежат в основе gradient accumulation, A/B-тестов и Брезенхэма
Связанные темы
Операции с дробями ведут в численные методы и вероятности.
- Десятичные дроби — Альтернативная запись и floating-point арифметика
- НОК и НОД — Инструмент общего знаменателя и сокращения
- Пропорции — Равенство двух дробей как rate в A/B-тестах
- Введение в вероятности — Произведение независимых событий повторяет умножение дробей
Вопросы для размышления
- Почему для умножения дробей не нужен общий знаменатель, а для сложения - нужен?
- Почему деление на дробь меньше единицы даёт результат больше делимого?
- Какая связь между gradient accumulation в PyTorch и приведением дробей к общему знаменателю?
Связанные уроки
- ar-06-fractions-intro — Базовое определение дроби и эквивалентность
- ar-17-lcm — НОК - инструмент общего знаменателя
- ar-08-decimals — Десятичная запись и floating-point
- ar-10-proportions — Равенство дробей как пропорция и rate
- prob-01-intro — Произведение независимых вероятностей повторяет умножение дробей