Бинарный алгоритм НОД

Бинарный алгоритм НОД , также известный как алгоритм Штейна или бинарный алгоритм Евклида , [1] [2] — это алгоритм, который вычисляет наибольший общий делитель (НОД) двух неотрицательных целых чисел. Алгоритм Штейна использует более простые арифметические операции, чем обычный алгоритм Евклида ; он заменяет деление арифметическими сдвигами , сравнениями и вычитанием.
Хотя алгоритм в его современном виде был впервые опубликован израильским физиком и программистом Йозефом Штейном в 1967 году. [3] возможно, он был известен во II веке до нашей эры в древнем Китае. [4]
Алгоритм
[ редактировать ]Алгоритм находит НОД двух неотрицательных чисел. и неоднократно применяя эти тождества:
- : все делит ноль, и самое большое число, которое делит .
- : является общим делителем.
- если странно: тогда не является общим делителем.
- если странный и .
Поскольку НОД коммутативен ( ), эти идентификаторы по-прежнему применяются, если операнды поменяны местами: , если это странно и т. д.
Выполнение
[ редактировать ]Хотя приведенное выше описание алгоритма математически корректно, производительные программные реализации обычно отличаются от него по нескольким заметным признакам:
- избегая пробного деления на в пользу одного битового сдвига и примитива подсчета конечных нулей ; это функционально эквивалентно многократному применению тождества 3, но гораздо быстрее;
- выражение алгоритма итеративно, а не рекурсивно : результирующую реализацию можно спланировать так, чтобы избежать повторной работы, вызывая идентификатор 2 в начале и сохраняя инвариантом то, что оба числа нечетны при входе в цикл, который должен реализовать только идентификаторы 3 и 4;
- делая тело цикла свободным от ветвей, за исключением условия выхода ( ): в приведенном ниже примере обмен и (обеспечение ) компилируется в условные ходы ; [5] труднопрогнозируемые ветки могут оказать большое негативное влияние на производительность. [6] [7]
Ниже приведена реализация алгоритма на Rust, иллюстрирующая эти различия, адаптированная из uutils :
use std::cmp::min;
use std::mem::swap;
pub fn gcd(mut u: u64, mut v: u64) -> u64 {
// Base cases: gcd(n, 0) = gcd(0, n) = n
if u == 0 {
return v;
} else if v == 0 {
return u;
}
// Using identities 2 and 3:
// gcd(2ⁱ u, 2ʲ v) = 2ᵏ gcd(u, v) with u, v odd and k = min(i, j)
// 2ᵏ is the greatest power of two that divides both 2ⁱ u and 2ʲ v
let i = u.trailing_zeros(); u >>= i;
let j = v.trailing_zeros(); v >>= j;
let k = min(i, j);
loop {
// u and v are odd at the start of the loop
debug_assert!(u % 2 == 1, "u = {} should be odd", u);
debug_assert!(v % 2 == 1, "v = {} should be odd", v);
// Swap if necessary so u ≤ v
if u > v {
swap(&mut u, &mut v);
}
// Identity 4: gcd(u, v) = gcd(u, v-u) as u ≤ v and u, v are both odd
v -= u;
// v is now even
if v == 0 {
// Identity 1: gcd(u, 0) = u
// The shift by k is necessary to add back the 2ᵏ factor that was removed before the loop
return u << k;
}
// Identity 3: gcd(u, 2ʲ v) = gcd(u, v) as u is odd
v >>= v.trailing_zeros();
}
}
Примечание. Приведенная выше реализация принимает беззнаковые (неотрицательные) целые числа; при условии , подписанный случай можно обработать следующим образом:
/// Computes the GCD of two signed 64-bit integers
/// The result is unsigned and not always representable as i64: gcd(i64::MIN, i64::MIN) == 1 << 63
pub fn signed_gcd(u: i64, v: i64) -> u64 {
gcd(u.unsigned_abs(), v.unsigned_abs())
}
Сложность
[ редактировать ]Асимптотически алгоритм требует шаги, где - это количество битов в большем из двух чисел, поскольку каждые два шага уменьшают хотя бы один из операндов как минимум в раз. . Каждый шаг включает в себя всего несколько арифметических операций ( с небольшой константой); при работе с числами размером в слово каждая арифметическая операция преобразуется в одну машинную операцию, поэтому количество машинных операций порядка , то есть .
Для сколь угодно больших чисел асимптотическая сложность этого алгоритма равна , [8] поскольку каждая арифметическая операция (вычитание и сдвиг) включает в себя линейное количество машинных операций (по одной на слово в двоичном представлении чисел). Если числа могут быть представлены в памяти машины, т. е. каждого числа размер может быть представлен одним машинным словом, эта граница сводится к:
Это то же самое, что и для алгоритма Евклида, хотя более точный анализ Ахави и Валле показал, что двоичный НОД использует примерно на 60% меньше битовых операций. [9]
Расширения
[ редактировать ]Алгоритм двоичного НОД можно расширить несколькими способами: либо для вывода дополнительной информации, более эффективной работы с целыми числами произвольного размера , либо для вычисления НОД в областях, отличных от целых чисел.
Расширенный двоичный алгоритм НОД , аналог расширенного алгоритма Евклида , подходит для первого типа расширения, поскольку он предоставляет коэффициенты Безу : целые числа. в дополнение к НОД и такой, что . [10] [11] [12]
В случае больших целых чисел лучшая асимптотическая сложность равна , с стоимость -битовое умножение; это почти линейно и значительно меньше, чем алгоритм двоичного НОД. , хотя конкретные реализации превосходят старые алгоритмы только для чисел размером более 64 килобит ( т. е. более 8×10 19265 ). Это достигается за счет расширения двоичного алгоритма НОД с использованием идей алгоритма Шёнхаге – Штрассена для быстрого умножения целых чисел. [13]
Алгоритм двоичного НОД также был распространен на области, отличные от натуральных чисел, такие как целые гауссовы числа . [14] Целые числа Эйзенштейна , [15] квадратичные кольца, [16] [17] и целочисленные кольца числовых полей . [18]
Историческое описание
[ редактировать ]Алгоритм вычисления НОД двух чисел был известен в древнем Китае, при династии Хань , как метод сокращения дробей:
Если возможно, сократите его вдвое; в противном случае возьмите знаменатель и числитель, вычтите меньшее из большего и сделайте это поочередно, чтобы они стали одинаковыми. Уменьшите на такое же количество.
- Фангтянь - Землеустройство, Девять глав математического искусства
Фраза «если возможно, сократите вдвое» двусмысленна, [4]
- если это применимо, когда любое из чисел становится четным, алгоритм представляет собой двоичный алгоритм НОД;
- если это применимо только тогда, когда оба числа четные, алгоритм аналогичен алгоритму Евклида .
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Брент, Ричард П. (13–15 сентября 1999 г.). Двадцатилетний анализ двоичного алгоритма Евклида . 1999 Симпозиум Оксфорд-Майкрософт в честь профессора сэра Энтони Хоара. Оксфорд.
- ^ Брент, Ричард П. (ноябрь 1999 г.). Дальнейший анализ Бинарного алгоритма Евклида (Технический отчет). Вычислительная лаборатория Оксфордского университета. arXiv : 1303.2772 . ПРГ ТР-7-99.
- ^ Стейн, Дж. (февраль 1967 г.), «Вычислительные проблемы, связанные с алгеброй Рака», Журнал вычислительной физики , 1 (3): 397–405, Бибкод : 1967JCoPh...1..397S , doi : 10.1016/0021-9991 (67)90047-2 , ISSN 0021-9991
- ↑ Перейти обратно: Перейти обратно: а б Кнут, Дональд (1998), Получисловые алгоритмы , Искусство компьютерного программирования , том. 2 (3-е изд.), Аддисон-Уэсли, ISBN 978-0-201-89684-8
- ^ Годболт, Мэтт. «Проводник компиляторов» . Проверено 4 февраля 2024 г.
- ^ Капур, Раджив (21 февраля 2009 г.). «Как избежать стоимости неверного прогноза ветвей» . Зона разработчиков Intel .
- ^ Лемир, Дэниел (15 октября 2019 г.). «Неправильно предсказанные ветки могут увеличить время работы» .
- ^ «GNU MP 6.1.2: Двоичный НОД» .
- ^ Ахави, Али; Валле, Бриджит (2000), «Средняя битовая сложность евклидовых алгоритмов» , Proceedings ICALP'00, Конспекты лекций Computer Science 1853 : 373–387, CiteSeerX 10.1.1.42.7616
- ^ Кнут 1998 , с. 646, ответ к упражнению 39 раздела 4.5.2.
- ^ Менезес, Альфред Дж.; ван Оршот, Пол К.; Ванстон, Скотт А. (октябрь 1996 г.). «§14.4 Алгоритмы наибольшего общего делителя» (PDF) . Справочник по прикладной криптографии . ЦРК Пресс. стр. 606–610. ISBN 0-8493-8523-7 . Проверено 9 сентября 2017 г.
- ^ Коэн, Анри (1993). «Глава 1: Фундаментальные теоретико-числовые алгоритмы». Курс вычислительной алгебраической теории чисел . Тексты для аспирантов по математике . Том. 138. Шпрингер-Верлаг . стр. 17–18. ISBN 0-387-55640-0 .
- ^ Стеле, Дэмиен; Циммерманн, Пол (2004), «Двоичный рекурсивный алгоритм НОД» (PDF) , Алгоритмическая теория чисел , Конспекты лекций по Comput. наук, том. 3076, Springer, Берлин, стр. 411–425, CiteSeerX 10.1.1.107.8612 , doi : 10.1007/978-3-540-24847-7_31 , ISBN 978-3-540-22156-2 , MR 2138011 , S2CID 3119374 , Отчет об исследованиях INRIA RR-5050 .
- ^ Вейлерт, Андре (июль 2000 г.). «(1+i)-арное вычисление НОД в Z[i] как аналог алгоритма двоичного НОД» . Журнал символических вычислений . 30 (5): 605–617. дои : 10.1006/jsco.2000.0422 .
- ^ Дамгорд, Иван Бьерре; Франдсен, Гудмунд Сковбьерг (12–15 августа 2003 г.). Эффективные алгоритмы определения НОД и кубической невязки в кольце целых чисел Эйзенштейна . 14-й Международный симпозиум по основам теории вычислений. Мальмё , Швеция. стр. 109–117. дои : 10.1007/978-3-540-45077-1_11 .
- ^ Агарвал, Саураб; Франдсен, Гудмунд Сковбьерг (13–18 июня 2004 г.). Бинарный НОД-подобные алгоритмы для некоторых комплексных квадратичных колец . Симпозиум по алгоритмической теории чисел. Берлингтон, Вирджиния , США. стр. 57–71. дои : 10.1007/978-3-540-24847-7_4 .
- ^ Агарвал, Саураб; Франдсен, Гудмунд Сковбьерг (20–24 марта 2006 г.). Новый алгоритм НОД для колец квадратичных чисел с уникальной факторизацией . 7-й Латиноамериканский симпозиум по теоретической информатике. Вальдивия, Чили. стр. 30–42. дои : 10.1007/11682462_8 .
- ^ Викстрем, Дуглас (11–15 июля 2005 г.). Об l-арном НОД-алгоритме в кольцах целых чисел . Автоматы, языки и программирование, 32-й международный коллоквиум. Лиссабон, Португалия. стр. 1189–1201. дои : 10.1007/11523468_96 .
Дальнейшее чтение
[ редактировать ]- Кнут, Дональд (1998). «§4.5 Рациональная арифметика». Получисловые алгоритмы . Искусство компьютерного программирования . Том. 2 (3-е изд.). Аддисон-Уэсли. стр. 330–417. ISBN 978-0-201-89684-8 .
Охватывает расширенный двоичный НОД и вероятностный анализ алгоритма.
- Коэн, Анри (1993). «Глава 1: Фундаментальные теоретико-числовые алгоритмы». Курс вычислительной алгебраической теории чисел . Тексты для аспирантов по математике . Том. 138. Шпрингер-Верлаг . стр. 12–24. ISBN 0-387-55640-0 .
Охватывает различные темы, в том числе расширенный двоичный алгоритм НОД, который выводит коэффициенты Безу , эффективную обработку целых чисел различной точности с использованием варианта алгоритма НОД Лемера , а также связь между НОД и в непрерывную дробь разложением действительных чисел .
- Валле, Бриджит (сентябрь – октябрь 1998 г.). «Динамика бинарного алгоритма Евклида: функциональный анализ и операторы» . Алгоритмика . 22 (4): 660–685. дои : 10.1007/PL00009246 . S2CID 27441335 . Архивировано из оригинала (PS) 13 мая 2011 года.
Анализ алгоритма в среднем случае через призму функционального анализа : основные параметры алгоритмов представлены как динамическая система , а их среднее значение связано с инвариантной мерой системы передаточного оператора .
Внешние ссылки
[ редактировать ]- Словарь алгоритмов и структур данных NIST: двоичный алгоритм GCD
- Разрезание узла: двоичный алгоритм Евклида при разрезании узла
- Анализ двоичного евклидова алгоритма (1976), статья Ричарда П. Брента , включая вариант с использованием сдвигов влево