Euclidean_algorithms/eucl.py

90 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Обычные алгоритм Евклида:
# Основывается на утверждениях:
# 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 >> 1:
r1 = r0 - r1
x1 = x0 - x1
y1 = y0 - y1
# Инвариант который поддерживается на каждой итерации
print(f'{x0}*{a}+{y0}*{b}={r0}')
return x0, y0, r0