假设我在f(x)
和a
之间定义了一个函数b
。此函数可以有很多零,但也有很多渐近线。我需要检索所有此函数的零。最好的方法是什么?
实际上,我的策略如下:
我验证找到的零是否真的为零,或者这是否为渐近线
U = numpy.linspace(a, b, 100) # evaluate function at 100 different points
c = f(U)
s = numpy.sign(c)
for i in range(100-1):
if s[i] + s[i+1] == 0: # oposite signs
u = scipy.optimize.brentq(f, U[i], U[i+1])
z = f(u)
if numpy.isnan(z) or abs(z) > 1e-3:
continue
print('found zero at {}'.format(u))
这个算法似乎有效,除了我看到两个潜在的问题:
f(x) = x**2
这样的函数中)但是,我不认为它可以在函数I&#中出现39; m评估。您是否有更好的策略(仍然有效)来查找函数的所有零点?
我不认为这个问题很重要,但对于那些好奇的人,我会处理光纤中波传播的特征方程。该函数看起来像(之前定义了V
和ell
,ell
是一个正整数):
def f(u):
w = numpy.sqrt(V**2 - u**2)
jl = scipy.special.jn(ell, u)
jl1 = scipy.special.jnjn(ell-1, u)
kl = scipy.special.jnkn(ell, w)
kl1 = scipy.special.jnkn(ell-1, w)
return jl / (u*jl1) + kl / (w*kl1)
答案 0 :(得分:3)
为什么限于numpy
? Scipy有一个完全符合你想要的包:
http://docs.scipy.org/doc/scipy/reference/optimize.nonlin.html
我学到的一课:数字编程很难,所以不要这样做:)
无论如何,如果你已经开始自己构建算法了,那么我链接的scipy
上的文档页面(永远需要加载,顺便说一句)会为你提供一系列算法。我之前使用过的一种方法是将函数离散化为您的问题所必需的程度。 (也就是说,调整\ delta x使其比问题中的特征尺寸小得多。)这使您可以查找函数的特征(如符号的变化)。并且,您可以很容易地计算线段的衍生物(可能是从幼儿园开始),因此您的离散化函数具有明确定义的一阶导数。因为您已将dx调整为小于特征尺寸,所以保证不会错过对您的问题很重要的功能的任何功能。
如果您想知道“特征尺寸”的含义,请查看函数的某些参数,单位为长度或1 /长度。也就是说,对于某些函数f(x),假设x具有长度单位而f没有单位。然后寻找乘以x的东西。例如,如果要离散cos(\ pi x),则乘以x的参数(如果x具有长度单位)必须具有1 /长度的单位。所以cos(\ pi x)的特征尺寸是1 / \ pi。如果您的离散化远小于此,则不会出现任何问题。可以肯定的是,这个技巧并不总是有效,所以你可能需要做一些修补。
答案 1 :(得分:1)
我看到的主要问题是,如果你真的可以找到所有根 - 正如评论中已经提到的那样,这并不总是可行的。如果您确定您的功能不是完全病态的(已经提到sin(1/x)
),那么下一个是您对错过根或其中几个的容忍度。换句话说,它是你准备去的长度,以确保你没有错过任何 - 据我所知,没有一般的方法来为你隔离所有的根,所以你必须自己做。你展示的是一个合理的第一步。几条评论:
x_1
和x_2
,请在间隔[x_1+epsilon, x_2-epsilon]
中再次运行搜索。继续,直到找不到根(布伦特的方法保证收敛到 a 根,只要有一个)。 x
不要只检查f(x)
是否大,检查那,例如|f(x-epsilon/2)| > |f(x-epsilon)|
epsilon
的几个值x_e
(1e-8,1e-9,1e-10,类似的东西)。f(x_e)
检查{{1}}的值。< / LI>
答案 2 :(得分:0)
我发现使用scipy.optimize.fsolve来实现自己的root查找器相对容易。
想法:在间隔(start, stop)
中找到任何零,并通过更改step
反复调用fsolve
来逐步调整x0
的大小。使用相对较小的步长查找 all 的根。
只能在一维中搜索零(其他维必须固定)。如果您还有其他需求,建议您使用sympy来计算分析溶液。
注意:可能不会总是 找到 all 的零,但我看到它给出了相对较好的结果。我还将代码也放在了gist上,如有需要,我将对其进行更新。
import numpy as np
import scipy
from scipy.optimize import fsolve
from matplotlib import pyplot as plt
# Defined below
r = RootFinder(1, 20, 0.01)
args = (90, 5)
roots = r.find(f, *args)
print("Roots: ", roots)
# plot results
u = np.linspace(1, 20, num=600)
fig, ax = plt.subplots()
ax.plot(u, f(u, *args))
ax.scatter(roots, f(np.array(roots), *args), color="r", s=10)
ax.grid(color="grey", ls="--", lw=0.5)
plt.show()
Roots: [ 2.84599497 8.82720551 12.38857782 15.74736542 19.02545276]
import numpy as np
import scipy
from scipy.optimize import fsolve
from matplotlib import pyplot as plt
class RootFinder:
def __init__(self, start, stop, step=0.01, root_dtype="float64", xtol=1e-9):
self.start = start
self.stop = stop
self.step = step
self.xtol = xtol
self.roots = np.array([], dtype=root_dtype)
def add_to_roots(self, x):
if (x < self.start) or (x > self.stop):
return # outside range
if any(abs(self.roots - x) < self.xtol):
return # root already found.
self.roots = np.append(self.roots, x)
def find(self, f, *args):
current = self.start
for x0 in np.arange(self.start, self.stop + self.step, self.step):
if x0 < current:
continue
x = self.find_root(f, x0, *args)
if x is None: # no root found.
continue
current = x
self.add_to_roots(x)
return self.roots
def find_root(self, f, x0, *args):
x, _, ier, _ = fsolve(f, x0=x0, args=args, full_output=True, xtol=self.xtol)
if ier == 1:
return x[0]
return None
scipy.special.jnjn
不再存在,但是我为这种情况创建了类似的测试功能。
def f(u, V=90, ell=5):
w = np.sqrt(V ** 2 - u ** 2)
jl = scipy.special.jn(ell, u)
jl1 = scipy.special.yn(ell - 1, u)
kl = scipy.special.kn(ell, w)
kl1 = scipy.special.kn(ell - 1, w)
return jl / (u * jl1) + kl / (w * kl1)
答案 3 :(得分:0)
我也遇到了这个问题,无法解决f(z)= 0等式,其中f是全纯函数。我想确保不要错过任何零,并最终开发了一种基于argument principle的算法。
它有助于找到位于复杂域中的零的确切数目。一旦知道零的数目,就更容易找到它们。但是,必须考虑两个问题: