代数求解丢番图方程

使用SymPy以代数方法求解丢番图方程(找到多项式方程的整数解),如果可能,返回参数化的通解。例如,求解勾股定理方程 \(a^2 + b^2 = c^2\) 得到 \((a=2pq, b=p^2-q^2, c=p^2+q^2)\)。这里,\(p\)\(q\) 是解中引入的新参数。\(p\)\(q\) 可以取任意整数值来参数化所有解的集合。更正式地,\(p,q \in \mathbb{Z}\) 参数化了勾股数的无穷集合。

考虑的替代方案

寻找一个参数化的丢番图方程的一般解的替代方法很少。

  • 数值替代方案:

    • Sage 的 EllipticCurve 命令 可能能够为每个变量找到一组相对数值

    • 你可以测试显式的整数值,例如使用嵌套的值范围的for循环。这是低效的,但如果你只对相对较小的解感兴趣,这也没问题。

  • solve() 将变量视为实数或复数,并简单地根据其他变量求解一个变量,从而产生不同类型的解。例如,尝试求解 \(a^2 + b^2 = c^2\) 中的 \(a\)\(b\)\(c\),只能揭示 \(a = \pm \sqrt{c^2-b^2}\)

解丢番图方程的例子

这里是一个求解丢番图方程的例子,特别是 \(a^2 + b^2 = c^2\),使用 diophantine()

>>> from sympy.solvers.diophantine import diophantine
>>> from sympy import symbols, Eq
>>> a, b, c = symbols("a, b, c", integer=True)
>>> my_syms = (a, b, c)
>>> pythag_eq = Eq(a**2 + b**2, c**2)
>>> # Solve Diophantine equation
>>> d = diophantine(pythag_eq, syms=my_syms)
>>> d
{(2*p*q, p**2 - q**2, p**2 + q**2)}

更多关于解决各种类型丢番图方程的示例,请参阅 丢番图API参考

指导

丢番图方程可以表示为等于零的表达式

如果你已经有一个等于零的表达式,你可以解这个表达式。例如,将勾股定理表达为 \(a^2 + b^2 - c^2\) 也是有效的:

>>> from sympy.solvers.diophantine import diophantine
>>> from sympy import symbols
>>> a, b, c = symbols("a, b, c", integer=True)
>>> my_syms = (a, b, c)
>>> pythag = a**2 + b**2 - c**2
>>> diophantine(pythag, syms=my_syms)
{(2*p*q, p**2 - q**2, p**2 + q**2)}

指定结果中符号的顺序

我们建议您在结果中指定符号的顺序,以避免混淆。使用 syms 参数并传递一个符号的元组或列表,以确保结果将按该顺序排列,例如 syms=my_syms,如本页示例所示。

限制

目前,可以使用 diophantine() 和 Diophantine 模块的其他辅助函数来求解以下五种类型的丢番图方程。

  • 线性丢番图方程:\(a_1x_1 + a_2x_2 + \ldots + a_nx_n = b\)

  • 一般二元二次方程:\(ax^2 + bxy + cy^2 + dx + ey + f = 0\)

  • 齐次三元二次方程:\(ax^2 + by^2 + cz^2 + dxy + eyz + fzx = 0\)

  • 扩展的毕达哥拉斯方程:\(a_{1}x_{1}^2 + a_{2}x_{2}^2 + \ldots + a_{n}x_{n}^2 = a_{n+1}x_{n+1}^2\)

  • 平方和的一般形式:\(x_{1}^2 + x_{2}^2 + \ldots + x_{n}^2 = k\)

使用解决方案结果

从结果中提取表达式

diophantine() 返回结果为一个元组集合,其中每个元组中的元素是方程中变量的表达式。例如,对于勾股方程,结果是一个包含一个元组的集合,其中表达式对应于 (a, b, c)。也就是说,元组表示 a = 2*p*q, b = p**2 - q**2, c = p**2-q**2。由于不能通过下标从集合中提取元素(这里是一个元组),您可以创建一个符号-表达式对的字典,通过其符号提取表达式:

>>> from sympy.solvers.diophantine import diophantine
>>> from sympy import symbols
>>> a, b, c = symbols("a, b, c", integer=True)
>>> my_syms = (a, b, c)
>>> pythag = a**2 + b**2 - c**2
>>> solution, = diophantine(pythag, syms=my_syms)
>>> solution
(2*p*q, p**2 - q**2, p**2 + q**2)
>>> # Convert set to list
>>> solution_dict = dict(zip(my_syms, solution))
>>> solution_dict
{a: 2*p*q, b: p**2 - q**2, c: p**2 + q**2}
>>> # Extract an expression for one variable using its symbol, here a
>>> solution_dict[a]
2*p*q

不那么优雅地,你可以将集合转换为列表,然后对列表进行下标操作。忘记参数顺序是一个常见错误,因此这种方法更容易出错:

>>> from sympy.solvers.diophantine import diophantine
>>> from sympy import symbols
>>> a, b, c, p, q = symbols("a, b, c, p, q", integer=True)
>>> my_syms = (a, b, c)
>>> pythag = a**2 + b**2 - c**2
>>> d = diophantine(pythag, syms=my_syms)
>>> d
{(2*p*q, p**2 - q**2, p**2 + q**2)}
>>> # Convert set to list
>>> solution_list = list(d)
>>> solution_list
[(2*p*q, p**2 - q**2, p**2 + q**2)]
>>> # Extract a tuple corresponding to a solution
>>> solution_first = solution_list[0]
>>> solution_first
(2*p*q, p**2 - q**2, p**2 + q**2)
>>> # Extract an expression for one variable using its order, here a is element number zero
>>> solution_first[0]
2*p*q

使用参数

你可以操作诸如 pq 这样的参数,这些参数是由 diophantine() 自动生成的,通过将它们创建为符号。例如,要找到满足丢番图方程的特定一组值,你可以通过

  1. 将参数创建为符号

  2. 使用 subs() 替换它们的值。

在这里,我们将值集合表示为一个字典,以将每个变量(\(a, b, c\))与其示例值关联起来:

>>> from sympy.solvers.diophantine import diophantine
>>> from sympy import symbols
>>> my_syms = (a, b, c)
>>> pythag = a**2 + b**2 - c**2
>>> d = diophantine(pythag, syms=my_syms)
>>> solution_list = list(d)
>>> solution_list
[(2*p*q, p**2 - q**2, p**2 + q**2)]
>>> p, q = symbols("p, q", integer=True)
>>> # Substitute in values as the dictionary is created
>>> solution_p4q3 = dict(zip(my_syms, [var.subs({p:4, q:3}) for var in solution_list[0]]))
>>> solution_p4q3
{a: 24, b: 7, c: 25}

请注意,你需要为生成的参数(pq)包含 integer=True 假设,以便为它们代入数值。相反,你不需要为原始方程中的符号(abc)包含 integer=True 假设,尽管这是一个好的做法。

要迭代解决方案集,您可以在嵌套循环中迭代参数 (pq) 的值:

>>> from sympy.solvers.diophantine import diophantine
>>> from sympy import symbols
>>> a, b, c, p, q = symbols("a, b, c, p, q", integer=True)
>>> my_syms = (a, b, c)
>>> pythag = a**2 + b**2 - c**2
>>> d = diophantine(pythag, syms=my_syms)
>>> solution_list = list(d)
>>> # Iterate over the value of parameters p and q
>>> for p_val in range(-1,2):
...     for q_val in range(-1,2):
...         # Substitute in the values of p and q
...         pythag_vals = dict(zip(my_syms, [var.subs({p:p_val, q:q_val}) for var in solution_list[0]]))
...         # Print out the values of the generated parameters, and the Pythagorean triple a, b, c
...         print(f"p: {p_val}, q: {q_val} -> {pythag_vals}")
p: -1, q: -1 -> {a: 2, b: 0, c: 2}
p: -1, q: 0 -> {a: 0, b: 1, c: 1}
p: -1, q: 1 -> {a: -2, b: 0, c: 2}
p: 0, q: -1 -> {a: 0, b: -1, c: 1}
p: 0, q: 0 -> {a: 0, b: 0, c: 0}
p: 0, q: 1 -> {a: 0, b: -1, c: 1}
p: 1, q: -1 -> {a: -2, b: 0, c: 2}
p: 1, q: 0 -> {a: 0, b: 1, c: 1}
p: 1, q: 1 -> {a: 2, b: 0, c: 2}

验证一个解决方案

你可以通过将其整数值代入原始方程(等于零的表达式)并检查结果是否为零来验证解决方案是否正确,可以使用来自 Work With Parameters 的字典方法,或者通过手动代入由任何程序确定的值:

>>> from sympy.solvers.diophantine import diophantine
>>> from sympy import symbols
>>> a, b, c, p, q = symbols("a, b, c, p, q", integer=True)
>>> my_syms = (a, b, c)
>>> pythag = a**2 + b**2 - c**2
>>> d = diophantine(pythag, syms=my_syms)
>>> solution_list = list(d)
>>> solution_p4q3 = dict(zip(my_syms, [var.subs({p:4, q:3}) for var in solution_list[0]]))
>>> # Substitute values in using a dictionary
>>> pythag.subs({a: solution_p4q3[a], b: solution_p4q3[b], c: solution_p4q3[c]})
0
>>> # Manually substitute in values
>>> pythag.subs({a: 24, b: 7, c: 25})
0

以编程方式提取参数符号

如果你想以编程方式获取某个解决方案的自动生成参数集,可以使用以下代码:

>>> from sympy.solvers.diophantine import diophantine
>>> from sympy import symbols
>>> a, b, c, p, q = symbols("a, b, c, p, q", integer=True)
>>> my_syms = (a, b, c)
>>> pythag = a**2 + b**2 - c**2
>>> # Solve Diophantine equation
>>> solution, = diophantine(pythag, syms=my_syms)
>>> solution
(2*p*q, p**2 - q**2, p**2 + q**2)
>>> # Extract parameter symbols
>>> set().union(*(s.free_symbols for s in solution))
{p, q}

并非所有方程都能被求解

无解的方程

一些丢番图方程没有解,在这种情况下,diophantine() 将返回一个空集,set()。例如,在表达式 \(2x + 4y - 3\)(我们试图将其设为零)中,系数都是偶数(\(2\)\(4\)),因此项的和 \((2x + 4y)\) 只能是偶数。然而,常数 \(3\) 是奇数,所以没有解。

>>> from sympy.solvers.diophantine import diophantine
>>> from sympy import symbols
>>> x, y = symbols("x, y", integer=True)
>>> diophantine(2*x + 4*y - 3, syms=(x, y))
set()

报告一个错误

如果你在使用 diophantine() 时发现了一个错误,请在 SymPy 邮件列表 上发布这个问题。在问题解决之前,你可以使用 Alternatives to Consider 中列出的其他方法。