我想找到一个静止点列表,它们的值和位置,以及它们是最小值还是最大值。
我的功能如下:
import numpy as np
def func(x,y):
return (np.cos(x*10))**2 + (np.sin(y*10))**2
以下是我正在考虑使用的方法:
我实际上已经在Mathematica上做了类似的事情。我将功能区分为一次然后两次。我查看一阶导数为0的点,计算它们的值及其位置。然后我在这些位置采用二阶导数并检查它们是最小值还是最大值。
我也想知道是否只是在x和y中创建函数值的二维数组,并找到该数组的最大值和最小值。但这需要我知道如何精确地定义x和y网格以可靠地捕获函数的行为
对于后一种情况,我已经找到了一些方法,如this one。
我只是想知道,哪种方法在Python的效率,速度,准确性甚至优雅方面更有意义?
答案 0 :(得分:1)
找到静止点的列表,它们的值和位置,以及它们是最小值还是最大值。
这通常是一个无法解决的问题。方法1(符号)适用于此,但对于复杂函数,没有静止点的符号解(没有方法用于象征性地求解两个方程的一般系统)。
对于像您的示例这样的简单函数,SymPy可以正常工作。这是一个完整的例子,用于找到静止点并通过Hessian的特征值对它们进行分类。
import sympy as sym
x, y = sym.symbols("x y")
f = sym.cos(x*10)**2 + sym.sin(y*10)**2
gradient = sym.derive_by_array(f, (x, y))
hessian = sym.Matrix(2, 2, sym.derive_by_array(gradient, (x, y)))
到目前为止,Hessian是一个符号矩阵2乘2:[[200*sin(10*x)**2 - 200*cos(10*x)**2, 0], [0, -200*sin(10*y)**2 + 200*cos(10*y)**2]]
。接下来,我们通过将gradient
等于零来找到静止点,并将它们逐个插入Hessian。
stationary_points = sym.solve(gradient, (x, y))
for p in stationary_points:
value = f.subs({x: p[0], y: p[1]})
hess = hessian.subs({x: p[0], y: p[1]})
eigenvals = hess.eigenvals()
if all(ev > 0 for ev in eigenvals):
print("Local minimum at {} with value {}".format(p, value))
elif all(ev < 0 for ev in eigenvals):
print("Local maximum at {} with value {}".format(p, value))
elif any(ev > 0 for ev in eigenvals) and any(ev < 0 for ev in eigenvals):
print("Saddle point at {} with value {}".format(p, value))
else:
print("Could not classify the stationary point at {} with value {}".format(p, value))
最后一个条款是必要的,因为当Hessian只有半明确时,我们无法分辨出哪种静止点(x**2 + y**4
和x**2 - y**4
具有相同的Hessian at(0,0)但行为不同)。输出:
Saddle point at (0, 0) with value 1
Local maximum at (0, pi/20) with value 2
Saddle point at (0, pi/10) with value 1
Local maximum at (0, 3*pi/20) with value 2
Local minimum at (pi/20, 0) with value 0
Saddle point at (pi/20, pi/20) with value 1
Local minimum at (pi/20, pi/10) with value 0
Saddle point at (pi/20, 3*pi/20) with value 1
Saddle point at (pi/10, 0) with value 1
Local maximum at (pi/10, pi/20) with value 2
Saddle point at (pi/10, pi/10) with value 1
Local maximum at (pi/10, 3*pi/20) with value 2
Local minimum at (3*pi/20, 0) with value 0
Saddle point at (3*pi/20, pi/20) with value 1
Local minimum at (3*pi/20, pi/10) with value 0
Saddle point at (3*pi/20, 3*pi/20) with value 1
显然,solve
没有找到所有解决方案(其中有无数的解决方案)。考虑solve vs solveset,但无论如何,处理无限多的解决方案很难。
SciPy提供了很多numerical minimization routines,包括brute force(这是你的方法2;通常它非常慢)。这些是强大的方法,但请考虑这些要点。
minimize
的参数x0)可能会产生另一个最大值或最小值。尽管如此,你永远不会知道还有其他你还未见过的极值。 使用lambdify
可以将符号表达式转换为可以传递给SciPy数值解算器的Python函数。
from scipy.optimize import fsolve
grad = sym.lambdify((x, y), gradient)
fsolve(lambda v: grad(v[0], v[1]), (1, 2))
这会在此示例中返回一些静止点[0.9424778 , 2.04203522]
。它取决于最初的猜测,即(1,2)。通常(但并非总是),您将获得一个接近初始猜测的解决方案。
这比直接最小化方法具有优势,因为也可以检测鞍点。尽管如此,找到所有解决方案仍然很困难,因为fsolve
的每次运行只会带来一个。