如何用三个未知数和数百个解决方案解决Python的非线性方程?

时间:2015-05-27 13:56:33

标签: python numpy scipy nonlinear-optimization

我正在尝试使用python在类型的非线性方程中找到三个未知数(x,y,z)的值:

g(x) * h(y) * k(z) = F

其中F是具有数百个值的向量。

我成功使用了scipy.optimize.minimize,其中F只有3个值,但是当F的大小大于3时失败。

如何使用F中的所有值找到(x,y,z)?到目前为止,唯一可用的方法是使用网格搜索,但效率非常低(参见#Find unknown x [0],x [1],x [2])。在python中是否有一个函数可以用来查找x [0],x [1],x [2]而不是使用网格搜索?

这里是代码:

import numpy as np

#  Inputs:
thetas = np.array([25.4,65,37,54.9,26,21.3,24.1,35.7,46.1,61.1,57.2,41.9,20.5,24,55.6,56.9,42.2,39.9,30.8,59,28.8])
thetav = np.array([28.7,5.4,22.6,14.4,23.5,25,12.8,31.2,15.3,9,7.4,24.4,29.7,15.3,15.5,26.8,8.8,16.6,25.1,18.5,12])
azs =    np.array([130.3,158,150.2,164.8,152.4,143.5,144.2,151.8,167.4,169.7,162.2,161.4,138.2,147.8,172.9,168.6,158.3,159.8,151.7,160.8,144.5])
azv =    np.array([55.9,312.8,38.6,160.4,324.2,314.5,236.3,86.1,313.3,2.1,247.6,260.4,118.9,199.9,277,103.1,150.5,339.2,35.6,14.7,24.9])
F =   np.array([0.61745,0.43462,0.60387,0.56595,0.48926,0.55615,0.54351,0.64069,0.54228,0.51716,0.39157,0.51831,0.7053,0.62769,0.21159,0.29964,0.52126,0.53656,0.575,0.40306,0.60471])

relphi = np.abs(azs-azv)

thetas = np.deg2rad(thetas)
thetav = np.deg2rad(thetav)
relphi = np.deg2rad(relphi)


#  Compute the trigonometric functions:
coss = np.abs (np.cos(thetas))
cosv = np.cos(thetav)
sins = np.sqrt(1.0 - coss * coss)
sinv = np.sqrt(1.0 - cosv * cosv)
cosp = -np.cos(relphi)
tans = sins / coss
tanv = sinv / cosv
csmllg = coss * cosv + sins * sinv * cosp
bigg = np.sqrt(tans * tans + tanv * tanv - 2.0 * tans * tanv * cosp)


# Function to solve
def fun(x):
    return x[0] * ((coss * cosv) ** (x[1] - 1.0)) * ((coss + cosv) ** (x[1] - 1.0)) * (1.0 - x[2] * x[2]) / ((1.0 + x[2] * x[2] + 2.0 * x[2] * csmllg) ** (1.5) + 1e-12) * (1.0 + ((1 - x[0]) / (1.0 + bigg))) - F


# Find unknown x[0], x[1], x[2]

n_bins=51
rho0_min=0.0
rho0_max=2.0
rho0_index=np.linspace(rho0_min, rho0_max, n_bins,retstep=True)
k_min=0.0
k_max=2.0
k_index=np.linspace(k_min, k_max, n_bins,retstep=True)
bigtet_min=-1.0
bigtet_max=1.0
bigtet_index=np.linspace(bigtet_min, bigtet_max, n_bins,retstep=True)
results=np.zeros((4,n_bins**3))
minima=np.ones(4)
RMSE_th = 0.001
index_while=0
current_RMSE=1.0
while current_RMSE > RMSE_th:
    index_results=0
    for rho0 in rho0_index[0]:
        for k in k_index[0]:
            for bigtet in bigtet_index[0]:
                results[:,index_results] = [rho0, k, bigtet, np.sqrt(np.sum((surf-func([rho0,k,bigtet]))**2) / surf.size)]
                index_results=index_results+1
    minima = results[:,np.argmin(results[3,:])]

    if (index_while > 10) or ((current_RMSE-minima[3]) < RMSE_th/100.0):
        break
    else:
        current_RMSE=minima[3]
        index_while=index_while+1
        rho0_min=minima[0]-2*rho0_index[1]
        rho0_max=minima[0]+2*rho0_index[1]
        rho0_index=np.linspace(rho0_min, rho0_max, 11,retstep=True)
        k_min=minima[1]-2*k_index[1]
        k_max=minima[1]+2*k_index[1]
        k_index=np.linspace(k_min, k_max, 11,retstep=True)
        bigtet_min=minima[2]-2*bigtet_index[1]
        bigtet_max=minima[2]+2*bigtet_index[1]
        bigtet_index=np.linspace(bigtet_min, bigtet_max, 11,retstep=True)


rho0= minima[0]
k= minima[1]
bigtet= minima[2]
print (rho0,k,bigtet,minima[3])

return (rho0,k,bigtet)

2 个答案:

答案 0 :(得分:0)

啊,啊,我明白了。您正在使用scipy.optimize.minimize,它将函数fun(x)作为要最小化的函数。但是根据documentation最小化执行&#34;最小化一个或多个变量的标量函数。&#34;。这里的线索是标量。你需要返回一个标量。一种经典的方法是最小化平方偏差的总和:

deviations = x[0] * ((coss * cosv) ** (x[1] - 1.0)) * ((coss + cosv) ** (x[1] - 1.0)) * (1.0 - x[2] * x[2]) / ((1.0 + x[2] * x[2] + 2.0 * x[2] * csmllg) ** (1.5) + 1e-12) * (1.0 + ((1 - x[0]) / (1.0 + bigg))) - F

我强烈建议你开始将表达式切成碎片,以便更容易理解。然后,函数的返回可以是:

return sum( deviations ** 2)

答案 1 :(得分:0)

正如@Jblasco建议的那样,你可以减少平方和。 scipy.leastsq()专为此类问题而设计。对于您的示例,代码将是:

import scipy.optimize as sopt

xx0 = np.array([0., 0., 0.])  # starting point
rslt = sopt.leastsq(fun, xx0, full_output=True)
print("The solution is {}".format(rslt[0]))

查看rslts的其他条目,了解有关解决方案质量的信息。请记住,数字可以在你身上耍花招,特别是当有指数和数百个变量时。如果您遇到问题,请从Scipy查看另一个optimizers。同时给出明确的jacobians(作为leastsq()的参数)可以提供帮助。