[英]How to solve a pair of nonlinear equations using Python?
使用 Python 求解一對非線性方程的(最佳)方法是什么。(Numpy、Scipy 或 Sympy)
例如:
- x+y^2 = 4
- e^x+ xy = 3
解決上述對的代碼片段會很棒
對於數值解,您可以使用 fsolve:
http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fsolve.html#scipy.optimize.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]
第一個參數是方程列表,第二個是變量列表,第三個是初始猜測。
正如其他答案中提到的,對您提出的特定問題的最簡單解決方案是使用類似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 ⎦
試試這個,我向你保證它會完美運行。
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 秒。
而且我建議你不要使用 Sympy 的功能 <- 確實很方便,但就速度而言,它很慢。 你會看見。
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]
確認解決方案是正確的。
我得到了 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 近似求函數的根。
這種方法也被稱為“Broyden 的好方法”。
您可以使用 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])
您可以使用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),所以我們可以認為它們為零,這意味着根都是實數。 Re ~ 13602.938,D ~ 0.047922 和 f~0.0057。
最好的方法是編寫自己的求解器。 並進化它。
嘗試使用 for 循環以小增量更改 x 和 y 值,例如 eps=0.0001
然后嘗試使用斜率修改器 err/eps 反饋錯誤
然后根據你接近的速度調整修改器 eps ---> 在這里你已經自己達到了牛頓迭代......
繼續以數字方式工作...引入多個維度,這將自動產生偏微分(您接近的速度)
您將接觸到梯度下降、單純形和類似的優化方法。
始終將您的數值求解器與 sympy 的分析結果進行比較
我認為這是最好的方法,因為您可以根據自己的需要和情況進行調整。 你會在知情的情況下做事,並抓住問題。
只有在此之后,您才能選擇與您的求解器進行比較的求解器。 或者您可以並排使用它們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.