详解扩展欧几里得算法:
引入:
1.求不定方程的通解:
2.欧几里得定理:(可以参考:链接)
3.裴蜀定理:
4.问题来了:如何求出方程ax+by=gcd(a,b)的一个特解呢?
其中gcd(a,b)为a, b的最大公约数,我们假设为 d ;
先看这段代码(求a, b的最大公约数)
def gcd(a, b):
if b==0: # 终止条件
return a
return gcd(b, a%b) # return 直到将最终结果 a 带出去
def main():
a = gcd(8251, 6105) # a 就是最大公约数
print(a)
main()
接着想:递归到最后一层时, 该层的 a =gcd(a,b),b=0;所以对于此时的方程:ax+by = gcd(a,b)
有一个特解(1,0);
注意:此时的方程是当前层的方程,他和咱们要求的第一层的方程并不同(是指a,b不同;但是gcd(a,b)是相同的;因为gcd(a,b)=gcd(b,a%b))
那么问题又来了:如何根据最后一层推导出上一层,让其迭代出来呢?
敲黑板:(注意在递归算法中,永远都是先得到下面一个状态的值)
假设 当前层 的状态为 ax+by = gcd(a,b) ; 那么根据 代码 执行到 下一层 时的状态是什么呢?
——————————————————————————————————————
没错:b*x + (a%b)*y = gcd(b, a%b)
又知道:a%b = (a - (a/b) b) 其中a/b为向下取整
那么 bx + (a%b)y = gcd(b, a%b) 可以化简为:ay + b*(x - (a/b)*y) = gcd(b, a%b)
假设这一层的特解为( y, x - (a/b)*y )
我们知道这一层的 解 是从上一层(即:当前层)得来的;
——————————————————————————————————————
所以当前层的 解(x, y)应该根据( y, x - (a/b)*y ) 来转换状态
所以:对上面的代码增加相应状态转换的代码:
x = 0 # 这里将x, y定义为全局变量:因为每一层操作的都是同一个x, y
y = 0 # 而 a,b 对于每一层来说是独立的
def exgcd(a, b):
global x, y
if b==0:
x = 1
y = 0
return a
d = exgcd(b, a%b)
tmp = y # 下面三行代码即为 “状态转换”
y = x-int(a/b)*y
x = tmp
return d # d 为最大公约数; 逐层return 回归出去,回归到 d = exgcd(b, a%b) 这里;最终return
# 回归到main函数
#(递归就是 递推+回归)
5.下面找个题练一练:
6.那么还有一个问题:如果让求ax+by = c 的一个特解,如何求呢?
显然要分类讨论:
1.如果 c 不是gcd(a, b)的整数倍,自然不存在解(裴蜀定理)
2.如果 c 是gcd(a, b)的整数倍:
请看下图:
7.最后一个问题:
我们知道 特解(x, y)有可能出现负数;如何得到最小正整数解呢?
对得到的x进行:
x = ( x % b + b ) % b;(求出的就是最小正整数解) |
---|
然后根据 方程 ax+by = c 求得 y 值
8.找个提练一练:
1.练一练吧!!!!!!嘻嘻!!代码稍后给出!
2.对于负数的处理
# 第一题 (不知为何,这一题在这个平台AC不了,c++可以AC)
x = 0
y = 0
def exgcd(a, b):
global x, y
if b==0:
x = 1
y = 0
return a
d = exgcd(b, a%b)
tmp = y
y = x-int(a/b)*y
x = tmp
return d
pass
def main():
global x, y
row = eval(input())
for i in range(row):
line = input().split(" ")
a, b, m= eval(line[0]), eval(line[1]), line[2]
d = exgcd(a, b)
if b%d!=0:
print("impossible")
continue
x *= int(b/d)
print(x)
return
main()
# 第二题 已经AC
x = 0
y = 0
def exgcd(a, b):
global x, y
if b==0:
x = 1
y = 0
return a
d = exgcd(b, a%b)
tmp = y
y = x-int(a/b)*y
x = tmp
return d
def main():
global x, y
line = input().split(" ")
a = eval(line[0])
b = eval(line[1])
c = eval(line[2])
k = eval(line[3])
for i in range(int(k/c)):
d = exgcd(a, b)
num = k - c*i
if (num % d)!=0:
continue
x = x*(num/d)
y = y*(num/d)
z = i
x = (x%b+b)%b
y = (num-a*x)/b
if x>=0 and y>=0:
print("{} {} {}".format(int(x), int(y), i))
break
else:
return
#return
main()