Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Теоретико-числовые алгоритмы в криптографии.pdf
Скачиваний:
236
Добавлен:
23.03.2015
Размер:
2.46 Mб
Скачать

Глава 10. Целочисленная арифметика многократной точности

§ 10.1. Введение. Сложение и вычитание

В данной главе мы описываем основные алгоритмы для выполнения арифметических операций с большими целыми числами, а также некоторые алгоритмы в кольцах вычетов Z/nZ.

Мы считаем, что числа записаны в b-ичной системе счисления, где b — фиксированное натуральное число, b 2. При этом натуральное число, записываемое не более чем n цифрами в b-ичной системе счисления, мы обозначаем u1 . . . un (допуская, что несколько старших разрядов u1, . . . , uk могут равняться нулю). Основание b не всегда равно 2; иногда оно соответствует размеру машинного слова, отведенному под запись обычных целых чисел. В этом случае мы работаем с массивом, содержащим большое целое число.

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

Заметим, что программы, реализующие арифметику многократной точности, лучше всего писать на ассемблере.

Опишем сложение и вычитание.

Алгоритм А (сложение неотрицательных целых чисел).

Для двух неотрицательных чисел u1 . . . un и v1 . . . vn вычисляется их сумма w0 . . . wn; при этом w0 — цифра переноса — всегда равна 0 или 1.

1 шаг. Присвоить j := n, k := 0 (здесь j идет по разрядам, k следит за переносом).

2 шаг. Присвоить wj := uj + vj + k (mod b), wj — наименьший неотрицательный вычет в данном классе вычетов;

k = uj + vj + k . b

(Заметим, что wj — очередная цифра, k — перенос; всегда k = 0 или k = 1. При этом, если b = 2 или b — размер машинного слова, то для

§ 10.2. Умножение

255

вычисления wj и k не нужно использовать деления, достаточно взять соответствующий разряд (или разряды) в записи uj + vj + k.)

3 шаг. Присвоить j := j − 1. Если j > 0, то идти на шаг 2; если j = 0, то присвоить w0 := k и закончить работу.

Конец алгоритма.

Обоснование корректности алгоритма очевидно.

Алгоритм S (вычитание неотрицательных целых чисел).

По двум n-разрядным неотрицательным целым числам u= u1 . . . unv = v1 . . . vn 0 вычисляется их разность w = w1 . . . wn = u − v.

Замечание 10.1. Для того, чтобы в общем случае установить, что u1 . . . un v1 . . . vn, надо пройти по цифрам, вычисляя uj − vj. Это простая проверка; с ее помощью находится знак разности u − v в общем случае.

1 шаг. Присвоить j := n, k := 0 (переменная k — это заем из старшего разряда).

2 шаг. Присвоить wj := uj − vj + k (mod b) — наименьший неотрицательный вычет в данном классе вычетов;

= uj − vj + k k : .

b

3 шаг. j := j − 1. Если j > 0, то идти на шаг 2; при j = 0 закончить работу.

Конец алгоритма.

Обоснование алгоритма достаточно очевидно. При j = n мы находим wn = un − vn, если un vn, и wn = b + un − vn, если un < vn. Соответственно, k = 0 и k = 1 — это заем из n − 1 разряда. Дальнейшие рассуждения аналогичны.

§ 10.2. Умножение

Здесь мы опишем несколько методов умножения целых чисел, приводящих к эффективным на практике алгоритмам.

Алгоритм M (умножение неотрицательных целых чисел столбиком).

Для чисел u = u1 . . . un и v = v1 . . . vm в системе счисления с основанием b мы находим их произведение w = uv = w1 . . . wm+n.

1 шаг. Присвоить wm+1 := 0, . . . , wm+n := 0, j := m. (Значение j перемещается по номерам разрядов v от младших к старшим.)

· bl.

256 Гл. 10. Целочисленная арифметика многократной точности

2 шаг. Если vj = 0, то присвоить wj := 0 и перейти на шаг 6. (Этот шаг можно пропустить. Однако если b мало, например, b = 2, то vj равно нулю с достаточно большой вероятностью. В этом случае выполнение шага 2 дает значительную экономию.)

3 шаг. Присвоить i := n, k := 0. (Значение i идет по номерам разрядов числа u, k отвечает за перенос.)

4 шаг. Присвоить t := ui · vj + wi+j + k, wi+j := t (mod b) — наименьший неотрицательный вычет в данном классе вычетов, k := [t/b]. (По прежнему, как и в § 10.1, при вычислении wi+j и k в ряде случаев можно обходиться без деления. Легко показать, что выполняются неравенства 0 t < b2, 0 k < b.)

5 шаг. i := i − 1. Если i > 0, то идти на шаг 4. Если i = 0, то присвоить wj := k.

6 шаг. j := j − 1. Если j > 0, то идти на шаг 2. Если j = 0, то закончить работу.

Конец алгоритма.

Проведем обоснование корректности алгоритма по индукции. Пусть j = m. Если на 2 шаге vm = 0, то в последних n + 1 раз-

рядах w будут стоять нули. Если же vm > 0, то на 4-м шаге мы фактически находим uibn−ivmbm−m = uivjbm+n−(i+j) и добавляем в разряд wi+j с учетом переноса k. При этом определяется истинное значение цифры wi+j и очередной перенос k. В итоге при j = m после шагов 2—5 мы нашли wmwm+1 . . . wm+n = u · vn. Это основание индукции.

Теперь предположим, что мы прошли 2—6 шаги l раз и верно определили u · (vm−l+1vm−l+2 . . . vm) = wm−(l−1) wm−(l−2) . . . wm+n. Тогда после 6 шага j = m − l, мы уходим на 2 шаг и вычисляем u · vm−l Снова идем по разрядам un, . . . , u1, находим uibn−ivm−lbl = uivjbn+l−i и соответственно изменяем цифру с номером n + m − (n + l − i) = i + j. На этом мы завершим наше краткое обоснование алгоритма М.

Теперь мы опишем метод умножения, предложенный Комбой, см. [92; 91]. Мы будем называть этот метод «быстрым столбиком». Нам

нужно умножить u=u1 . . .un = n

uibn−i на v=v1 . . .vm = n

vjbm−j. Будем

считать, что n m. В

i=1

j=1

 

умноже

 

алгоритме M нам требовалось кроме

-

 

mn

 

ний uivj и некоторого количества сложений определенное количество чтений записей памяти. А именно, элементы vj мы читали m раз, элементы ui мы читали mn раз (по n раз при каждом фиксированном j), элементы wi+j мы mn раз читали и mn раз записывали для них новые значения. То есть нам нужно mn умножений и около 3mn чтений

§ 10.2. Умножение

257

записей памяти. В алгоритме M мы вычисляли uv по формуле

 

 

 

 

uv =

uvjbm−j.

 

 

 

 

 

 

(10.1)

 

 

 

 

 

 

=1

 

 

 

 

 

 

 

 

 

 

 

 

 

j

 

 

 

 

 

 

 

 

Однако справедлива и другая формула:

 

 

 

 

 

 

 

 

 

m+n−2

s

un−ivm−s+i

,

 

 

 

 

(10.2)

 

 

uv =

s=0

bs i=0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

где при l 0 мы полагаем ul = vl = 0. В самом деле,

 

 

 

 

m+n−2

bs

 

 

 

 

n−1

 

 

 

 

 

bs =

 

 

uv =

u

v

m−j

=

 

 

u

v

m−s+i

 

 

s=0

i+j=s

n−i

 

 

 

 

n−i

 

 

 

 

 

 

 

 

i=0 i s m−1+i

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0 i n−1

 

 

 

m+n 2

 

 

 

 

 

 

 

 

0 j m−1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

=

bs

 

 

u

v

 

.

(10.3)

 

 

 

 

 

 

 

 

m−s+i

 

 

 

 

 

 

=0

s m+1 i

s

n−i

 

 

 

 

 

 

 

 

 

s

 

 

 

 

 

 

 

Формула (10.3) эквивалентна

формуле (10.2). Действительно, ес-

ли s − m + 1 < 0,

т. е. s < m − 1, то

при

i < 0

величина un−i

рав-

на 0 по определению. Если же s − m + 1 > 0, т. е. s > m − 1, то при 0 i < s − m + 1 значение i + m − s < 1, и vm−s+i = 0 по определению.

Теперь воспользуемся формулой (10.2), чтобы умножить u на v.

Алгоритм FM («быстрый столбик»)..

1

шаг. t := 0.

2

шаг. (цикл) Для s от 0 до m + n − 1 с шагом 1 выполнить ша-

ги 3 и 4.

3

шаг. Для i от 0 до s с шагом 1 выполнить присвоение

 

t := t + un−i · vm−s+i.

4

шаг. Присвоить wm+n−s := t (mod b) — наименьший неотрица-

тельный вычет по модулю b (опять-таки, это не деление, а чтение записи памяти, если b = 2 или b — размер машинного слова);

t := [t/b].

Конец алгоритма.

Корректность алгоритма следует из формулы (10.2).

Операций умножения здесь столько же, сколько в алгоритме М, поскольку каждое ui надо умножить на каждое vj, только в другом порядке. Однако количество чтений записей памяти сокращается: мы читаем ui и vj столько раз, сколько перемножаем — т. е. 2mn раз (mn раз

17 О. Н. Василенко

258

Гл. 10. Целочисленная арифметика многократной точности

читаем ui и mn раз читаем vj). И еще требуется m + n записей в память значений wm+n−s. В итоге число чтений записей памяти в «быстром столбике» существенно меньше, чем в алгоритме М, и реализация алгоритма FM дает реальный выигрыш во времени на практике (если писать программу на ассемблере и если b — это размер слова в ассемблере).

Теперь опишем метод Карацубы для умножения целых чисел (см. [23]). Предположим, что у нас имеется два 2n-разрядных числа в двоичной системе счисления:

u = u2n−1 . . . u0,

v = v2n−1 . . . v0.

Положим u = 2nu + u , v = 2nv + v , где

u = u2n−1 . . . un,

u = un−1 . . . u0,

v = v2n−1 . . . vn,

v = vn−1 . . . v0.

Тогда

 

 

 

 

 

 

 

 

 

 

 

uv =

(22n

+

2n)

 

+ 2n (

) (

 

− v

) + (2n + 1)

 

. (10.4)

 

 

u v

u

− u v

 

 

u v

 

Поэтому для умножения двух 2n-разрядных чисел по формуле (10.4) нужно три умножения n-разрядных чисел и O(1) сложений, вычитаний и сдвигов 4n-разрядных чисел, которые делаются за O(n) битовых операций. Если обозначить через T (n) число битовых операций для умножения двух n-разрядных чисел, то

T (2n) 3T (n) + cn,

(10.5)

где c — некоторая абсолютная постоянная. Из (10.5) по индукции следует неравенство

T (2k) c(3k 2k), k = 1, 2, 3, . . .

(10.6)

Действительно, при k = 1 T (2) c (это обеспечивается за счет выбора постоянной c). Далее, если (10.6) верно для k, то

T (2k+1) 3T (2k) + c2k 3c(3k 2k) + c2k = c(3k+1 2k+1).

Поскольку умножение двух n-разрядных чисел сводится к умножению 2[log2 n]+1-разрядных чисел (старшие биты при необходимости полагаем равными нулю), то из (10.6) получим неравенство

T (n) T (2[log2 n]+1) c3[log2 n]+1 c13log2 n = c1nlog2 3.

Это есть оценка сверху количества битовых операций, требуемых для умножения двух n-разрядных чисел методом Карацубы.