НОД, НОК и алгоритм Евклида - Preparation to IOI Part 1
Теория чисел
НОД, НОК и алгоритм Евклида
НОД, НОК и алгоритм Евклида
Теория
Урок 1 из 1
8 мин чтения
Наибольший общий делитель
В этом уроке все входные данные — неотрицательные целые числа.
Наибольший общий делитель (НОД), gcd(a,b), — это наибольшее положительное целое число, которое делит и a, и b, если хотя бы одно из них не равно нулю.
Любое положительное целое число делит ноль. Поэтому при a>0 общие делители чисел a и — это в точности делители числа , откуда . Также по соглашению полагаем : в этом случае наибольшего положительного общего делителя не существует.
Числа можно уменьшать, не меняя их общих делителей. При b>0 разделим a на b с остатком и запишем a=qb+r, где q — частное, а 0≤r<b — остаток.
Любое число, которое делит и a, и b, также делит r=a−qb. И наоборот: любое число, которое делит и b, и r, делит a=qb+r. Значит, пары (a,b) и (b,r) имеют одни и те же общие делители:
gcd(a,b)=gcd(b,amodb).
Алгоритм Евклида многократно выполняет эту замену. Второй аргумент на каждом шаге уменьшается, поэтому в конце концов становится равным нулю. Тогда первый аргумент и есть ответ.
На схеме показана работа алгоритма для пары (84,30). В каждой строке первое число разбивается на несколько частей, равных второму числу, и остаток. Синий остаток становится вторым аргументом в следующей строке.
Интерактивпопробуй!
Почему алгоритм работает быстро
Второй аргумент не обязательно уменьшается вдвое за один шаг: на схеме он сначала уменьшается с 30 до 24. Однако за два шага он уменьшается хотя бы вдвое.
Если a<b, первый шаг меняет числа местами. Когда a≥b>0, обозначим r=amodb:
Если r≤b/2, нужное уменьшение происходит уже за первый шаг.
Если r>b/2, то r<b<2r, поэтому следующий остаток равен bmodr=b−r<b/2.
Таким образом, алгоритм выполняет O(log(2+min(a,b))) операций взятия остатка, в том числе в случаях с нулевыми входными данными.
Задание
Для положительных a и b рассмотрим другой процесс: будем вычитать меньшее число из большего, пока они не станут равными. Сохраняет ли этот процесс НОД? Остаётся ли число итераций логарифмическим? Обоснуйте оба ответа.
Проверить рассуждения
НОД сохраняется: при a>b пары (a,b) и (a−b,b) имеют одни и те же общие делители. Другой случай разбирается аналогично. Оба числа остаются положительными, а их сумма уменьшается, поэтому процесс заканчивается, когда числа становятся равными. Их общее значение — исходный НОД.
Однако оценка числа итераций другая. Для пары (m,1) потребуется m−1 вычитаний. Операция взятия остатка в алгоритме Евклида сразу убирает все полные копии меньшего числа, поэтому для такой пары достаточно одного шага.
Наименьшее общее кратное
НОД связан с общими делителями, а наименьшее общее кратное (НОК), lcm(a,b), — это наименьшее положительное целое число, которое делится на оба положительных входных числа. Если хотя бы одно из них равно нулю, полагаем НОК равным нулю, в том числе lcm(0,0)=0.
Чтобы установить связь между НОД и НОК, рассмотрим разложение на простые множители. Простое число — это целое число не меньше 2, у которого нет положительных делителей, кроме 1 и самого себя. Каждое положительное целое число единственным образом раскладывается на простые множители; у числа 1 простых множителей нет.
Для простого числа p обозначим через αp и βp его показатели степени в разложениях a и b. Если p отсутствует в разложении, соответствующий показатель равен нулю. В общем делителе показатель степени p не может превышать min(αp,βp), а в НОД он равен именно этому значению. В общем кратном показатель должен быть не меньше max(αp,βp), и в НОК он равен именно этому значению.
При умножении НОД на НОК эти показатели складываются. Для каждого простого числа их сумма равна αp+βp, поэтому произведение равно ab. Следовательно, для положительных a,b:
lcm(a,b)=gcd(a,b)ab.
Как избежать переполнения
Сначала выполняйте деление, затем умножение. Произведение ab может вызвать переполнение, даже если сам НОК помещается в выбранный тип. При этом a/gcd(a,b) — целое число, не превышающее a.
Но и итоговое произведение может оказаться слишком большим. Пусть M — наибольшее значение типа long long, заданное константой LLONG_MAX, а x=a/gcd(a,b). При b>0 умножение x на b безопасно тогда и только тогда, когда
x≤⌊bM⌋.
Проверяйте это неравенство до умножения. Нулевые аргументы нужно обработать заранее, чтобы ни в этой проверке, ни при делении на НОД не возникло деления на ноль.
НОД и НОК списка чисел
Обе операции ассоциативны: результат не зависит от расстановки скобок. Число делит все элементы тогда и только тогда, когда оно делит их НОД. Число является общим кратным всех элементов тогда и только тогда, когда оно кратно их НОК. Эти свойства не зависят от группировки элементов. Принятые соглашения для нуля также сохраняют ассоциативность.
Чтобы найти НОД непустого списка a1,…,an, начните с g=0 и последовательно обрабатывайте элементы:
g←gcd(g,ai).
Начальный ноль подходит, потому что gcd(0,ai)=ai. Как только g=1, дальнейшие элементы уже не могут изменить результат.
Для НОК сначала проверьте, есть ли в списке ноль. Если есть, ответ равен нулю. Иначе начните с ℓ=1 и последовательно находите НОК текущего ℓ и очередного элемента, каждый раз проверяя переполнение. Каждый новый НОК — положительное кратное предыдущего. Поэтому, если промежуточный результат превысил допустимый предел, итоговый результат тоже его превысит.
Если вычисление прекращается при первом переполнении, предварительная проверка на ноль необходима: ноль в оставшейся части списка сделал бы итоговый ответ равным нулю.
Подумайте
Пусть a,b,c — положительные целые числа и gcd(a,b,c)=1. Обязательно ли lcm(a,b,c)=abc? Если нет, приведите контрпример. Найдите точное условие на НОД каждой пары чисел, при котором это равенство выполняется.
Проверить рассуждения
Возьмём a=6, b=10 и c=15. Их НОД равен 1, но НОК равен 30, тогда как abc=900. Ни одно простое число не делит все три числа, но каждый простой множитель встречается в двух из них.
Равенство выполняется тогда и только тогда, когда НОД каждой пары равен 1: такие числа называют попарно взаимно простыми. Если у чисел нет общих простых множителей, наибольший показатель степени каждого простого числа совпадает с его показателем в произведении. Если же простое число делит хотя бы два числа, его показатель в произведении строго больше наибольшего показателя в разложении отдельного числа. Поэтому произведение больше НОК.
Реализация на C++
Оба аргумента должны быть неотрицательными и помещаться в тип long long. Функция lcm_safe возвращает −1, если НОК превышает LLONG_MAX.
Показать реализацию
In this lesson
Вычислять НОД с помощью алгоритма Евклида и объяснять, почему каждый шаг сохраняет ответ.
Доказывать формулу НОК с помощью разложения на простые множители.
Вычислять НОК с проверкой переполнения 64-битного целого типа.
Находить НОД и НОК списка чисел, в том числе содержащего нули.
#include <bits/stdc++.h>using namespace std;long long gcd(long long a, long long b){ while(b > 0){ long long r = a % b; a = b; b = r;} return a;}long long lcm_safe(long long a, long long b){ if(a == 0 || b == 0){ return 0;} a /= gcd(a, b); if(a > LLONG_MAX / b){ return -1;} return a * b;}