代数求解丢番图方程

使用 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}\)

求解丢番图方程的示例

以下是如何使用 diophantine() 求解丢番图方程(特别是 \(a^2 + b^2 = c^2\))的示例。

>>> 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() 和丢番图模块的其他辅助函数求解以下五种类型的丢番图方程。

  • 线性丢番图方程:\(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}

验证解

你可以通过将解的整数值代回到原始方程(等于零的表达式)中并检查结果是否为零来验证解是否正确,方法是使用 使用参数 中的字典方法,或手动代入由任何过程确定的值。

>>> 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 邮件列表 上发布问题。在问题解决之前,你可以使用 其他方法 中列出的其他方法。