Числа №4. Натуральные дроби
Прежде чем мы перейдем к числам с плавающей запятой, напомню о натуральных дробях. Иногда с их помощью можно решить задачу, не прибегая к флоатам.
Натуральная дробь — это отношение двух целых чисел, например 1/2, 1/3, 4/9 и так далее. Их достоинство в том, что иногда из них можно выйти обратно к целым числам. Например, кто-то меняет коров на лошадей по курсу 2/3, то есть за две коровы — три лошади. В случае с флоатами курс был бы 0.666666… или 0.(6) в периоде. Далее нам дают 6 лошадей. Умножаем 6 на 0.66666… и получаем 3.999996 коровы. Округляем до четырех и совершаем обмен. Но если учесть, что:
- коров и лошадей может быть много;
- операции совершаются часто;
- операции строятся в цепочку: коровы на лошадей, лошади на кур и все это по разным курсам,
то погрешность рано или поздно даст о себе знать.
Если хранить курс обмена в виде натуральной дроби 2/3, то умножение на 6 даст целое число:
2 6 12
- * - = -- = 4
3 1 3
Легко написать класс для работы с натуральными дробями. Это пара целых чисел. Операции над парой повторяют школьную программу. Чтобы сложить две дроби, их проводят к общему знаменателю. Например, чтобы сложить 1/6 и 1/12, мы:
- приводим 1/6 к знаменателю 12 и получаем 2/12;
- складываем числители;
- сокращаем дробь.
1 1 2 1 3 1
- + -- = -- + -- = -- = -
6 12 12 12 12 4
Вычитание — то же самое, только меняется знак. Умножение несложное: перемножаем числители и знаменатели, затем сокращаем результат. Деление аналогично, только вторая дробь переворачивается.
Для всех этих действий нужны две вещи: наименьшее общее кратное (НОК) и наибольший общий делитель (НОД). Принцип их поиска был найден еще греками.
В Кложе из коробки идет тип Ratio, который устроен как пара чисел
BigInteger. Если я поделю одно целое на другое, и результат дает остаток, то
получу дробь:
(/ 2 3)
2/3 ;; clоure.lang.Ratio
Умножив дробь на целое, которое сокращает знаменатель, я получу целое:
(* (/ 2 3) 6)
4 ;; clоjure.lang.BigInt
Никаких 3.999996 коровы, все честно. Здесь Рич Хикки ничего не придумал, а взял реализацию из Common Lisp, который еще до вашего рождения считал натуральные дроби из коробки.
Понимаю, что примеры с лошадьми звучат забавно. Но есть область, где натуральные дроби в высшей степени оправданы — недвижимость. Доли собственников хранятся в натуральных дробях, например у Васи — 1/2, у Пети — 1/3, у Маши — 1/6. В сумме они дают единицу. Предположим, Вася поделил свою долю 1/2 между Колей и Жорой: каждый получил по 1/4 от общей площади. Сумма долей по-прежнему дает единицу.
В недвижимости бывают и более сложные доли. Скажем, недавно я участвовал в сделке, где моя доля была 49/125. Так получилось в результате наследства, выкупа долей у других собственников и так далее.
Приступая к делению, подумайте: можно ли выразить его натуральной дробью. Не всегда это возможно, но если все-таки да, вы обезопасите себя от проблем, связанных с флотами. Об этих проблемах мы поговорим в следующих заметках.
Нашли ошибку? Выделите мышкой и нажмите Ctrl/⌘+Enter