Длинная арифметика

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

Применение

Длинная арифметика применяется в следующих областях:

Необходимые аппаратные средства для работы с длинной арифметикой

Строго говоря, для реализации арифметики произвольной точности от процессора требуется лишь косвенная адресация; в арифметике фиксированной точности можно обойтись даже без неё. Тем не менее, определённые функции процессора ускоряют длинную арифметику, одновременно упрощая её программирование.

Реализация в языках программирования

Языки программирования имеют встроенные типы данных, размер которых, в основном, не превышает 64 бита (около 1019). Десятичная длинная арифметика была реализована в советских языках программирования АЛМИР-65 на ЭВМ МИР-1 и АНАЛИТИК на ЭВМ МИР-2. Для работы с большими числами, в современных языках программирования существует довольно много готовых оптимизированных библиотек для длинной арифметики.

Большинство функциональных языков позволяют переключаться с обычной арифметики на длинную без необходимости изменения кода арифметических расчётов. Например, Erlang и Scheme всегда представляют точные числа длинными. В Standard ML реализации всех разновидностей целых чисел определяются на основании сигнатуры INTEGER, позволяя выбирать необходимую размерность,— в том числе присутствует модуль IntInf, реализующий целые числа произвольной точности; в реализации PolyML этот модуль используется по умолчанию.

Встроенные библиотеки работы с большими числами есть в PascalABC.NET, Ruby, Python и Java.

Алгоритмы

Алгоритмы умножения

Для описания алгоритмов обычно используется следующее представление целых чисел. Выбирается основание . Тогда целое число длины можно представить в виде[1]:

где

Базовый

Представляет собой алгоритм по школьному методу «в столбик». Занимает время где  — размеры перемножаемых чисел.

Алгоритм может быть представлен в виде[1][2]:

Algorithm 1 BasecaseMultiply
Input:
Output:

for from to do

Умножение Карацубы

Метод умножения Карацубы относится к парадигме «разделяй и властвуй». Вычислительная сложность алгоритма:

.

Данный алгоритм представляет собой простую реализацию[3] идеи разделения входных данных, которая стала базисной для нижеперечисленных алгоритмов. Идея заключается в разделении одной операции умножения над -значными числами на три операции умножения над числами длины плюс [1].

Для перемножения двух чисел, превышающих длину машинного слова, алгоритм Карацубы вызывается рекурсивно до тех пор, пока эти числа не станут достаточно маленькими, чтобы их можно было перемножить непосредственно. Пример такого алгоритма:

Algorithm 2 KaratsubaMultiply
Input:
Output:



KaratsubaMultiply
KaratsubaMultiply
KaratsubaMultiply

Посчитаем :

Нужно вычислить три промежуточных коэффициента:

Результат:

Алгоритм Тоома

Этот алгоритм является обобщением алгоритма Карацубы и работает быстрее. Для двух данных целых чисел и алгоритм Toom-a делит их на меньших частей, длина каждой из которых равна длине машинного слова, и производит операции над этими частями. Сложность вычисления алгоритма:

Алгоритм Тоома-3

Идея впервые была предложена А. Л. Тоомом в 1963 году[4], и заключается в разделении входных данных (множителей) на 3 равные части (для простоты все части считаем равными). Toom-3 сокращает число элементарных операций умножения с 9 до 5. Сложность алгоритма

Рассмотрим данный алгоритм на следующем примере. Пусть дано два числа и . Пусть определены операции над числами, размер которых равен 1/3 от размеров исходных чисел (см. рисунок). Предположим также, что числа занимают равную память и делятся ровно на 3 части, в противном случае дополним оба числа нулями до необходимых размеров.

Затем происходит отображение (параметризация) этих частей на полиномы 2 степени.

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

Также введем полином:

(1)

После того как будут определены элементы  — коэффициенты многочлена, они будут использованы в целях получения , так как . Размер коэффициентов в 2 раза больше (по памяти), чем разбиение или . Конечное число, равное произведению можно найти, складывая со сдвигом, как показано на рисунке ниже.

Коэффициенты могут быть вычислены следующим образом: и так далее, но это потребует все 9 перемножений: для i, j=0,1,2, и будет эквивалентно простому перемножению.

Вместо этого был использован иной подход. вычисляются в (1) в 5 точках при разных .

В таблице, представленной ниже, приведены значения полиномов в равенстве (1)

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

Алгоритм Тоома-4

Сложность алгоритма Представляет собой 7 элементарных операций умножения. Toom-4 разделяет входные данные на 4 части.

По такому же принципу как и в Toom-3 строим два полинома:

и вычисляются в 7 разных точках, также вычисляется значение в этих точках.

откуда сразу получаем
откуда сразу получаем

Количество операций сложения и умножения для Toom-4 намного больше, чем для Toom-3. Но некоторые выражения встречаются по нескольку раз. Например, вычисляется для и для [5].

Алгоритм Тоома для произвольного разбиения

Алгоритм Тоома для разделения входных чисел на n операндов эквивалентен описанному выше. В общем случае разделение двух одинаково длинных операндов на частей приводит к вычислению значений в точках. В качестве точек для решения системы, обычно берут .

Перемножение методом Фурье

Данный алгоритм перемножения используется для больших и очень больших чисел[6].

Этот алгоритм перемножает два числа за время где  — количество значащих цифр в перемножаемых числах (предполагая их равными). Создание приписывается Кули (Coolet) и Тюки (Tukey) — 1965 г. Также есть предположения, что данный алгоритм был известен и раньше, но не имел большой важности до того как изобрели первые компьютеры. Вероятными кандидатами на авторство изобретения данного алгоритма также называют Рунг (Runge) и Кёниг (Konig) — 1924 г., а также Гаусса — 1805 г.

Пусть имеется число, представим его в виде многочлена, как мы делали ранее. Назовем этот многочлен Также введем дискретное преобразование Фурье многочлена как вектор, с координатами . Где определены как  — комплексный корень -ой степени из 1, не равный 1. Дело в том, что комплексный корень из 1 определен с точностью до фазового множителя, количество этих корней — . Преобразование Фурье применено для того, чтобы свертку коэффициентов многочленов и :  — заменить на произведение их Фурье образов.

где под умножением подразумевается скалярное произведение векторов.

Предположим, что есть степень двойки.

Нахождение производится рекурсивным методом (разделяй и властвуй). Идея заключается в разбиении исходного многочлена на два многочлена,

Тогда .

Заметим, что среди всех чисел , только различных. Поэтому, ДПФ и будут -элементными. А так как ДПФ и состоит из элементов, то комплексным корнем из 1 там будет корень степени .

Значит,

где и

Мы использовали свойства комплексных чисел: различных корней степени всего . .

Получаем рекурсивный алгоритм:
ДПФ одного элемента равно этому элементу
для нахождения ДПФ A разбиваем коэффициенты A на чётные и нечётные, получаем два многочлена и , находим ДПФ для них, находим ДПФ :


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

Алгоритмы извлечения корня

Квадратный корень

Одним из алгоритмов вычисления квадратного корня является алгоритм «Karatsuba Square Root». На данный момент это самый известный метод вычисления квадратного корня n-значного числа. Этот алгоритм использует быстрое преобразование Фурье и метод Ньютона со сложностью 5M(n)[7].

Представленный алгоритм основан на делении Бурникеля — Циглера (Burnikel-Ziegler) Карацубы. Имея целое число n, алгоритм подсчитывает одновременно его целый квадратный корень и соответствующий остаток, который равен Это не является асимптотически оптимальным, но очень эффективно на практике со сложностью порядка операций, где K(n) — это количество операций, необходимых чтобы умножить два n-разрядных числа, используя алгоритм Карацубы (Karatsuba). Причина этой низкой сложности в красивом делении Карацубы (Karatsuba), недавно открытом Бурникелем и Циглером (Burnikel and Ziegler), а также в осторожном обращении с остатками, которое избегает ненужных вычислений.

Теорема. Число операций, которые алгоритм SqrtRem использует со 2n-разрядным входом, ограничено

где K(n) — число операций, необходимых для умножения 2n-разрядных чисел, используя алгоритм Карацубы.

Алгоритм SqrtRem 
    Вход:   with 
    Выход:  такие, что 
    
    
    
    if  then
         
         
    return 

Алгоритмы преобразования системы счисления

Предположим, что выполняется преобразование из системы счисления по основанию b в систему счисления по основанию В[8].

Способы преобразования целых чисел


Метод 1 (Деление на В с использованием представления чисел в формате по основанию b). Для заданного целого числа u можно
получить представление в формате по основанию В вида , выполняя

, , , до тех пор, пока не окажется .


Метод 2 (Умножение на В с использованием представления чисел в формате по основанию b). Если представление числа u по основанию b имеет вид , то можно, воспользовавшись арифметическими операциями с числами, которые представлены в формате по основанию В, получить полином в виде
(( .


Метод 1 (a) (Умножение на b с использованием представления чисел в формате по основанию В). Для данного дробного числа и можно вычислить значения разрядов его представления по основанию В следующим образом:
, , ,… где {х} означает xmod1 = х- . Чтобы округлить результат до М разрядов, вычисления можно прервать после получения , причем если {…{{uВ}В}...В} больше 1/2, то значение следует увеличить на единицу. (Заметим, однако, что эта операция может привести к необходимости выполнения переносов, которые должны быть при помощи арифметических операций по основанию В включены в результат. Было бы проще перед началом вычислений прибавить к исходному числу u константу , но это может привести к неправильному результату, если в компьютере число не может быть точно представлено в формате по основанию b. Заметим также, что возможно округление результата до , если ).
Метод 2 (a) (Умножение на В с использованием представления чисел в формате по основанию b). Если представление числа и по основанию b имеет вид , то можно, воспользовавшись арифметическими операциями с числами, которые представлены в формате по основанию В, получить полином в виде
((… + …) + .

Способы преобразования дробных чисел


Метод 1 (b) (Умножение на b с использованием представления чисел в формате по основанию В). Для данного дробного числа u можно вычислить значения разрядов его представления по основанию В следующим образом:
, , ,… где {х} означает xmod1 = х- . Чтобы округлить результат до М разрядов, вычисления можно прервать после получения , причем если {…{{uВ}В}...В} больше 1/2, то значение следует увеличить на единицу. (Заметим, однако, что эта операция может привести к необходимости выполнения переносов, которые должны быть при помощи арифметических операций по основанию В включены в результат. Было бы проще перед началом вычислений прибавить к исходному числу u константу , но это может привести к неправильному результату, если в компьютере число не может быть точно представлено в формате по основанию b. Заметим также, что возможно округление результата до , если ).


Метод 2 (b)(Деление на b с использованием представления чисел в формате по основанию В). Если число u представлено по основанию b в виде , то можно, используя арифметические операции по основанию В, вычислить в виде
. Необходимо внимательно следить за погрешностями, возникающими при усечениях или округлениях во время выполнения операции деления на b; они, как правило, незначительны, но это бывает не всегда.

Преобразование с многократной точностью


Начинать преобразование очень длинных чисел удобнее всего с преобразования блоков разрядов, операции с которыми можно выполнять с однократной точностью. Затем следует объединить эти блоки, пользуясь простыми способами, которые специфичны для многократной точности. Например, пусть 10n-наивысшая степень 10, меньшая, чем размер машинного слова. Тогда:
а) чтобы преобразовать целое "Число с многократной точностью из двоичного формата в десятичный, необходимо многократно разделить его на 10n (выполняя таким образом преобразование из двоичной системы счисления в десятичную с основанием 10n по методу 1, а); при помощи операций с однократной точностью получим n десятичных разрядов для каждой единицы представления в системе счисления с основанием 10n;
b) чтобы преобразовать дробную "Часть числа с многократной точностью из двоичного формата в десятичный, поступим подобным образом, умножив его на : (т.е. использовав метод 2, а, где В= 10n);
с) чтобы преобразовать целое число с многократной точностью из десятичной системы счисления в двоичную, преобразуем сначала блоки по n разрядов; затем для перехода из системы счисления с основанием 10n в двоичный формат используем метод 1, b;
d) для преобразования дробной части с многократной точностью из представления в десятичном формате в двоичный сначала выполним преобразование в систему с основанием 10n, как и в процедуре (с), а затем используем метод 2, b.

Алгоритмы деления

Алгоритм деления n-битового числа u на n-битовое число v должен выдавать результатом два n-битовых числа - u mod v и u div v. Описанные ниже алгоритмы не применимы на практике, так как находят вещественное число, а не два n-битовых числа, результата деления.

В теории чтобы разделить n-битовое число u на n - битовое число v, можно сначала найти n-битовое приближение к числу 1/v, затем умножить его на u, что даст приближение к , И наконец выполнить ещё одно умножение для внесения небольшой коррекции в , чтобы убедиться, что выполняется неравенство . Исходя из сказанного, достаточно иметь эффективный алгоритм, который формировал бы по заданному n-битовому числу приближенное значение числа, обратного n-битовому числу. Далее применить алгоритм умножения n-битовых чисел.[9]


Алгоритм R (Получение обратной величины с высокой точностью) Пусть число v имеет двоичное представление , где . Этот алгоритм вычисляет приближение z числа , такое, что
.
R1(Начальное приближение) Присвоить и .
R2 (Итерация по Ньютону) Здесь имеем число z в двоичном виде с знаками после разделяющей точки и . При помощи программы быстрого умножения точно вычислить . После этого точно вычислить , где . Затем присвоить , где , удовлетворяющее неравенству , прибавляется при необходимости для округления , чтобы было кратным . И наконец присвоить .
R3 Если , то вернуться к шагу R2; в противном случае выполнение алгоритма заканчивается.

Другие алгоритмы

Примечания

  1. 1 2 3 Richard P. Brent and Paul Zimmermann, Modern Computer Arithmetic
  2. Donald E. Knuth «The Art of Computer Programming», Секция 4.3.1
  3. Donald E. Knuth «The Art of Computer Programming», Секция 4.3.3, часть А
  4. Доклады академии наук СССР 150 (1963), 496—498
  5. Toom 4-Way Multiplication — GNU MP 5.1.3. Дата обращения: 13 декабря 2013. Архивировано 14 марта 2013 года.
  6. Donald E. Knuth «The Art of Computer Programming», Секция 4.3.3, часть С
  7. Paul Zimmermann. Karatsuba Square Root. [Research Report] RR-3805, 1999, pp.8. <inria-00072854 Архивная копия от 2 декабря 2014 на Wayback Machine>
  8. Donald E. Knuth «The Art of Computer Programming», том 2, Секция 4.4
  9. Donald E. Knuth «The Art of Computer Programming», том 2, Секция 4.3.3

Литература

  • Donald E. Knuth, «The Art of Computer Programming», volume 2, «Seminumerical Algorithms», 3rd edition, Addison-Wesley, 1998
  • Dan Zuras, «On Squaring and Multiplying Large Integers», ARITH-11: IEEE Symposium on Computer Arithmetic, 1993, pp. 260 to 271.
  • ДАН СССР 150 (1963), 496—498
  • Richard P. Brent and Paul Zimmermann, Modern Computer Arithmetic
  • Электронные средства и системы управления : Материалы докладов Международной научно-практической конференции (13-16 октября 2010 г.). — Томск: В-Спектр, 2011: В 2 ч. — Ч. 2. — 178 с. ISBN 978-5-91191-223-6, С.123-129