使用Python解决一对非线性方程式的(最佳)方法是什么?(Numpy,Scipy或Sympy)
例如:
- x + y ^ 2 = 4
- e ^ x + xy = 3
解决以上问题的代码片段会很棒
使用Python解决一对非线性方程式的(最佳)方法是什么?(Numpy,Scipy或Sympy)
例如:
- x + y ^ 2 = 4
- e ^ x + xy = 3
解决以上问题的代码片段会很棒
import sage
从任何Python脚本中获取。
Answers:
对于数值解,可以使用fsolve:
from scipy.optimize import fsolve
import math
def equations(p):
x, y = p
return (x+y**2-4, math.exp(x) + x*y - 3)
x, y = fsolve(equations, (1, 1))
print equations((x, y))
如果您喜欢sympy,则可以使用nsolve。
>>> nsolve([x+y**2-4, exp(x)+x*y-3], [x, y], [1, 1])
[0.620344523485226]
[1.83838393066159]
第一个参数是方程式列表,第二个参数是变量列表,第三个参数是初始猜测。
y = Symbol('symbol_name_string')
。可能您想要保留相同的符号名称,因此y = Symbol('y')
。
正如其他答案中提到的那样,针对您提出的特定问题的最简单解决方案是使用类似以下内容的方法fsolve
:
from scipy.optimize import fsolve
from math import exp
def equations(vars):
x, y = vars
eq1 = x+y**2-4
eq2 = exp(x) + x*y - 3
return [eq1, eq2]
x, y = fsolve(equations, (1, 1))
print(x, y)
输出:
0.6203445234801195 1.8383839306750887
您说了如何“解决”,但是有不同的解决方案。既然你提到SymPy我要指出什么之间这个最大的区别可能意味着这是之间的分析和数字解决方案。您给出的特定示例是一个没有(简单)解析解的示例,而其他非线性方程组则具有。当有现成的分析解决方案时,SymPY通常可以为您找到它们:
from sympy import *
x, y = symbols('x, y')
eq1 = Eq(x+y**2, 4)
eq2 = Eq(x**2 + y, 4)
sol = solve([eq1, eq2], [x, y])
输出:
⎡⎛ ⎛ 5 √17⎞ ⎛3 √17⎞ √17 1⎞ ⎛ ⎛ 5 √17⎞ ⎛3 √17⎞ 1 √17⎞ ⎛ ⎛ 3 √13⎞ ⎛√13 5⎞ 1 √13⎞ ⎛ ⎛5 √13⎞ ⎛ √13 3⎞ 1 √13⎞⎤
⎢⎜-⎜- ─ - ───⎟⋅⎜─ - ───⎟, - ─── - ─⎟, ⎜-⎜- ─ + ───⎟⋅⎜─ + ───⎟, - ─ + ───⎟, ⎜-⎜- ─ + ───⎟⋅⎜─── + ─⎟, ─ + ───⎟, ⎜-⎜─ - ───⎟⋅⎜- ─── - ─⎟, ─ - ───⎟⎥
⎣⎝ ⎝ 2 2 ⎠ ⎝2 2 ⎠ 2 2⎠ ⎝ ⎝ 2 2 ⎠ ⎝2 2 ⎠ 2 2 ⎠ ⎝ ⎝ 2 2 ⎠ ⎝ 2 2⎠ 2 2 ⎠ ⎝ ⎝2 2 ⎠ ⎝ 2 2⎠ 2 2 ⎠⎦
请注意,在此示例中,SymPy查找所有解决方案,不需要给出初始估计。
您可以使用以下方法对这些解决方案进行数字评估evalf
:
soln = [tuple(v.evalf() for v in s) for s in sol]
[(-2.56155281280883, -2.56155281280883), (1.56155281280883, 1.56155281280883), (-1.30277563773199, 2.30277563773199), (2.30277563773199, -1.30277563773199)]
但是,大多数非线性方程组系统都没有合适的解析解,因此,如上使用SymPy可以很好地工作,但通常不适用。这就是为什么即使有数值解,我们最终还是要寻找数值解的原因:1)我们不能保证我们找到了所有解或有很多解的“正确”解。2)我们必须提供一个初步的猜测,但这并不总是那么容易。
接受了我们想要数值解法之后,fsolve
通常会做您需要的所有事情。对于此类问题,SymPy可能会慢得多,但它可以提供其他一些可以更精确地找到(数字)解决方案的方法:
from sympy import *
x, y = symbols('x, y')
nsolve([Eq(x+y**2, 4), Eq(exp(x)+x*y, 3)], [x, y], [1, 1])
⎡0.620344523485226⎤
⎢ ⎥
⎣1.83838393066159 ⎦
精度更高:
nsolve([Eq(x+y**2, 4), Eq(exp(x)+x*y, 3)], [x, y], [1, 1], prec=50)
⎡0.62034452348522585617392716579154399314071550594401⎤
⎢ ⎥
⎣ 1.838383930661594459049793153371142549403114879699 ⎦
expr.evalf()
以下方法
试试这个,我向您保证它会完美运行。
import scipy.optimize as opt
from numpy import exp
import timeit
st1 = timeit.default_timer()
def f(variables) :
(x,y) = variables
first_eq = x + y**2 -4
second_eq = exp(x) + x*y - 3
return [first_eq, second_eq]
solution = opt.fsolve(f, (0.1,1) )
print(solution)
st2 = timeit.default_timer()
print("RUN TIME : {0}".format(st2-st1))
->
[ 0.62034452 1.83838393]
RUN TIME : 0.0009331008900937708
仅供参考。如上所述,您也可以通过将“ fsolve”替换为“ broyden1”来使用“ Broyden近似”。有用。我做的。
我不确切知道Broyden的近似值是如何工作的,但是花了0.02 s。
而且我建议您不要使用Sympy的功能<-确实很方便,但是就速度而言,它相当慢。你会看见。
我得到了Broyden的方法用于IDL中耦合非线性方程(通常涉及多项式和指数)的方法,但是我没有在Python中尝试过:
scipy.optimize.broyden1
scipy.optimize.broyden1(F, xin, iter=None, alpha=None, reduction_method='restart', max_rank=None, verbose=False, maxiter=None, f_tol=None, f_rtol=None, x_tol=None, x_rtol=None, tol_norm=None, line_search='armijo', callback=None, **kw)[source]
使用Broyden的第一个Jacobian逼近找到一个函数的根。
这种方法也被称为“布罗登的好方法”。
您可以使用openopt软件包及其NLP方法。它有许多动态编程算法来解决由非线性代数方程组:
goldenSection,scipy_fminbound,scipy_bfgs,scipy_cg,scipy_ncg,amsg2p,scipy_lbfgsb,scipy_tnc,bobyqa,ralg,ipopt,scipy_slsqp,scipy_cobyla,lincher,algencan,您可以从中选择。
后一种算法可以解决约束非线性规划问题。因此,您可以使用以下函数将方程组引入openopt.NLP():
lambda x: x[0] + x[1]**2 - 4, np.exp(x[0]) + x[0]*x[1]
from scipy.optimize import fsolve
def double_solve(f1,f2,x0,y0):
func = lambda x: [f1(x[0], x[1]), f2(x[0], x[1])]
return fsolve(func,[x0,y0])
def n_solve(functions,variables):
func = lambda x: [ f(*x) for f in functions]
return fsolve(func, variables)
f1 = lambda x,y : x**2+y**2-1
f2 = lambda x,y : x-y
res = double_solve(f1,f2,1,0)
res = n_solve([f1,f2],[1.0,0.0])
的替代方法fsolve
是root
:
import numpy as np
from scipy.optimize import root
def your_funcs(X):
x, y = X
# all RHS have to be 0
f = [x + y**2 - 4,
np.exp(x) + x * y - 3]
return f
sol = root(your_funcs, [1.0, 1.0])
print(sol.x)
这将打印
[0.62034452 1.83838393]
如果您再检查
print(your_funcs(sol.x))
你得到
[4.4508396968012676e-11, -1.0512035686360832e-11]
确认解决方案正确。
您可以使用nsolve
的sympy
意思numerical solver
。
示例片段:
from sympy import *
L = 4.11 * 10 ** 5
nu = 1
rho = 0.8175
mu = 2.88 * 10 ** -6
dP = 20000
eps = 4.6 * 10 ** -5
Re, D, f = symbols('Re, D, f')
nsolve((Eq(Re, rho * nu * D / mu),
Eq(dP, f * L / D * rho * nu ** 2 / 2),
Eq(1 / sqrt(f), -1.8 * log ( (eps / D / 3.) ** 1.11 + 6.9 / Re))),
(Re, D, f), (1123, -1231, -1000))
在哪里(1123, -1231, -1000)
找到根的初始向量。它给出了:
虚部很小,均为10 ^(-20),因此我们可以认为它们为零,这意味着根都是实数。稀土〜13602.938,D〜0.047922和f〜0.0057。