编辑:这里的一个大问题是scipy.optimize.brentq要求搜索间隔的限制具有相反的符号。如果您将搜索间隔分为任意部分,然后像我在下文中以及Dan在注释中所做的那样在每个部分上运行brentq,您最终会抛出很多无用的ValueErrors。有没有一种巧妙的方法可以在Python中处理此问题?

原始帖子:
我反复在python中寻找函数的最大零值。现在,我正在使用 scipy.optimize.brentq 来寻找根,然后如果我的初始边界不起作用,则使用一种残酷的搜索方法:

#function to find the largest root of f
def bigRoot(func, pars):
    try:
        root = brentq(func,0.001,4,pars)
    except ValueError:
        s = 0.1
        while True:
            try:
                root = brentq(func,4-s,4,pars)
                break
            except ValueError:
                s += 0.1
                continue
    return root

这有两个大问题。

首先,我假设如果一个间隔中有多个根,则brentq将返回最大根。我做了一些简单的测试,但除了最大的根以外,我从未见过它返回任何东西,但我不知道在所有情况下是否都如此。

第二个问题是,在我使用此函数的脚本中,即使传递给bigRoot的函数在0处发散,在某些情况下也始终返回零。如果我将搜索的步长从0.1更改为0.01,它将返回在这些情况下为恒定的非零值。我意识到具体细节取决于我传递给bigRoot的函数,但我认为问题可能出在我进行搜索的方式上。

问题是,在python中寻找函数最大根的更聪明的方法是什么?

谢谢丹;以下是一些更多信息,可根据要求提供。

我正在搜索的功能在我感兴趣的区域中表现良好。下面绘制了一个示例(帖子末尾的代码)。

唯一的奇点是0(从图的顶部离开的峰是有限的),并且有两个或三个根。最大的根通常不大于1,但是它从来不会像跑到无穷远那样做任何事情。根之间的间隔在域的低端变小,但它们永远不会变得非常小(我想说它们总是大于10 ^ -3)。
from numpy import exp as e
#this isn't the function I plotted
def V(r):
    return  27.2*(
                23.2*e(-43.8*r) +
                8.74E-9*e(-32.9*r)/r**6 -
                5.98E-6*e(-0.116*r)/r**4 +
                0.0529*( 23*e(-62.5*r) - 6.44*e(-32*r) )/r -
                29.3*e(-59.5*r)
            )

#this is the definition of the function in the plot
def f(r,b,E):
    return 1 - b**2/r**2 - V(r)/E

#the plot is of f(r,0.1,0.06)

最佳答案

好问题,但这是数学问题,而不是Python问题。

在缺少针对函数根的解析公式的情况下,即使在给定的有限间隔内,也无法保证找到该函数的最大根。例如,我可以构造一个函数,该函数在接近1时会在±1之间振荡。

f(x) = sin(1/(1-x))

这将使尝试在间隔[0,1)上找到最大根的任何数值方法都变得无用,因为对于任何根,在该间隔中总会有更大的根。

因此,您必须提供有关所讨论功能的特征的一些背景知识,以便对这一一般问题有更多的了解。

更新:看起来功能很好。 brentq文档建议不能保证在间隔中找到最大/最小的根。尝试对间隔进行分区,然后递归搜索其他越来越大的根。
from scipy.optimize import brentq

# This function should recursively find ALL the roots in the interval
# and return them ordered from smallest to largest.

from scipy.optimize import brentq
def find_all_roots(f, a, b, pars=(), min_window=0.01):
    try:
        one_root = brentq(f, a, b, pars)
        print "Root at %g in [%g,%g] interval" % (one_root, a, b)
    except ValueError:
        print "No root in [%g,%g] interval" % (a, b)
        return [] # No root in the interval

    if one_root-min_window>a:
        lesser_roots = find_all_roots(f, a, one_root-min_window, pars)
    else:
        lesser_roots = []

    if one_root+min_window<b:
        greater_roots = find_all_roots(f, one_root+min_window, b, pars)
    else:
        greater_roots = []

    return lesser_roots + [one_root] + greater_roots

我对您的函数进行了尝试,发现最大的根为〜0.14。

但是brentq有点棘手:
print find_all_roots(sin, 0, 10, ())

Root at 0 in [0,10] interval
Root at 3.14159 in [0.01,10] interval
No root in [0.01,3.13159] interval
No root in [3.15159,10] interval
[0.0, 3.141592653589793]
sin函数的根应为0,π,2π,3π。但是这种方法只能找到前两个。我意识到问题就在in the docs上: f(a)和f(b)必须具有相反的符号。 似乎scipy.optimize根查找函数的所有的都具有相同的要求,因此,任意划分间隔是行不通的。

10-04 11:44