90 lines
3.7 KiB
Python
90 lines
3.7 KiB
Python
|
|
|||
|
# Обычные алгоритм Евклида:
|
|||
|
# Основывается на утверждениях:
|
|||
|
# 1. gcd(a, b) = gcd(b, a % b) -> Лемма
|
|||
|
# 2. gcd(b, 0) = b -> Очевидно
|
|||
|
def eucl_rec(a, b):
|
|||
|
if b == 0:
|
|||
|
return a
|
|||
|
return eucl_rec(b, a % b)
|
|||
|
|
|||
|
def eucl_it(a, b):
|
|||
|
while a % b != 0:
|
|||
|
a, b = b, a % b
|
|||
|
return b
|
|||
|
###
|
|||
|
|
|||
|
# Расширенный алгоритм Евклида (вычисляются еще и коэф-ы линейного представления НОД)
|
|||
|
# Для gcd любых чисел есть такие целые x, y, что a * x + b * y = gcd(a, b)
|
|||
|
# Они тут и находятся параллельно с НОД
|
|||
|
def ext_eucl(a, b):
|
|||
|
r0, r1 = a, b
|
|||
|
x0, y0, x1, y1 = 1, 0, 0, 1
|
|||
|
while r1 != 0:
|
|||
|
## Вычисление коэффициентов
|
|||
|
q = r0 // r1
|
|||
|
x0, x1 = x1, x0 - q * x1 # Присваивания происходят одновременно
|
|||
|
y0, y1 = y1, y0 - q * y1 # Присваивания происходят одновременно
|
|||
|
##
|
|||
|
r1, r0 = r0 % r1, r1 # НОД вычисляется на этой строчке (см. утв. из обычного алг. Евклида)
|
|||
|
# Инвариант который поддерживается на каждой итерации
|
|||
|
print(f'{x0}*{a}+{y0}*{b}={r0}')
|
|||
|
return x0, y0, r0
|
|||
|
|
|||
|
def ext_eucl_rec(a, b):
|
|||
|
if b == 0:
|
|||
|
return a, 1, 0
|
|||
|
gcd, x, y = ext_eucl_rec(b, a % b)
|
|||
|
# Поддержание инварианта x*a+y*b=gcd(a, b)
|
|||
|
x = y
|
|||
|
# Объяснение почему x = y:
|
|||
|
# эта рекурсивная функция (если не считать x,y) выводит нечто такое
|
|||
|
# ?*6+?*3=3 подставляем 0,1 => будет 0*6+1*3=3
|
|||
|
# ?*9+?*6=3 подставляем 1,-1 => будет 1*9+-1*6=3
|
|||
|
# ?*15+?*9=3 подставляем -1,2 => будет -1*15+2*9=3
|
|||
|
# ?*39+?*15=3 подставляем 2,-5 => будет 2*39+-5*15=3
|
|||
|
# Можно заметить что X следующей строки становится игриком предыдущей
|
|||
|
# а вот Y уже надо подбирать
|
|||
|
y = (gcd - x*a) // b # просто выражаем y из формулы x*a+y*b=gcd(a, b)
|
|||
|
print(f'{x}*{a}+{y}*{b}={gcd}')
|
|||
|
return gcd, x, y
|
|||
|
|
|||
|
def bin_eucl_rec(a, b):
|
|||
|
# Базовый случай
|
|||
|
if a == 0:
|
|||
|
return b
|
|||
|
if b == 0:
|
|||
|
return a
|
|||
|
if (a & 1) == 0 and (b & 1) == 0: # Оба числа четные
|
|||
|
return bin_eucl_rec(a >> 1, b >> 1) << 1
|
|||
|
if (a & 1) == 0: # a четное, b нечетное
|
|||
|
return bin_eucl_rec(a >> 1, b)
|
|||
|
if (b & 1) == 0: # a нечетное, b четное
|
|||
|
return bin_eucl_rec(a, b >> 1)
|
|||
|
if a > b: # Оба числа нечетные и a > b
|
|||
|
return bin_eucl_rec(a - b, b)
|
|||
|
return bin_eucl_rec(b - a, a) # Оба числа нечетные и b > a
|
|||
|
|
|||
|
def ext_eucl_trunc(a, b):
|
|||
|
r0, r1 = a, b
|
|||
|
x0, y0, x1, y1 = 1, 0, 0, 1
|
|||
|
while r1 != 0:
|
|||
|
## Вычисление коэффициентов
|
|||
|
q = r0 // r1
|
|||
|
x0, x1 = x1, x0 - q * x1 # Присваивания происходят одновременно
|
|||
|
y0, y1 = y1, y0 - q * y1 # Присваивания происходят одновременно
|
|||
|
##
|
|||
|
r1, r0 = r0 % r1, r1 # НОД вычисляется на этой строчке (см. утв. из обычного алг. Евклида)
|
|||
|
|
|||
|
if r1 > r0 >> 2:
|
|||
|
r1 = r0 - r1
|
|||
|
x1 = x0 - x1
|
|||
|
y1 = y0 - y1
|
|||
|
|
|||
|
# Инвариант который поддерживается на каждой итерации
|
|||
|
print(f'{x0}*{a}+{y0}*{b}={r0}')
|
|||
|
return x0, y0, r0
|
|||
|
|
|||
|
|
|||
|
|