Numeric root-finding in Python

88888 Dihedral dihedral88888 at googlemail.com
Sun Feb 12 14:13:16 CET 2012


在 2012年2月12日星期日UTC+8下午2时41分20秒,Steven D'Aprano写道:
> This is only peripherally a Python problem, but in case anyone has any 
> good ideas I'm going to ask it.
> 
> I have a routine to calculate an approximation of Lambert's W function, 
> and then apply a root-finding technique to improve the approximation. 
> This mostly works well, but sometimes the root-finder gets stuck in a 
> cycle.
> 
> Here's my function:
> 
> import math
> def improve(x, w, exp=math.exp):
>     """Use Halley's method to improve an estimate of W(x) given 
>     an initial estimate w.
>     """
>     try:
>         for i in range(36):  # Max number of iterations.
>             ew = exp(w)
>             a = w*ew - x

W*EXP(W) can converge for negative values of W

>             b = ew*(w + 1)
b=exp(W)*W+W
>             err = -a/b  # Estimate of the error in the current w.

What's X not expalained?

>             if abs(err) <= 1e-16:
>                 break
>             print '%d: w= %r err= %r' % (i, w, err)
>             # Make a better estimate.
>             c = (w + 2)*a/(2*w + 2)
>             delta = a/(b - c)
>             w -= delta
>         else:
>             raise RuntimeError('calculation failed to converge', err)
>     except ZeroDivisionError:
>         assert w == -1
>     return w
> 
> 
> Here's an example where improve() converges very quickly:
> 
> py> improve(-0.36, -1.222769842388856)
> 0: w= -1.222769842388856 err= -2.9158979924038895e-07
> 1: w= -1.2227701339785069 err= 8.4638038491998997e-16
> -1.222770133978506
> 
> That's what I expect: convergence in only a few iterations.
> 
> Here's an example where it gets stuck in a cycle, bouncing back and forth
> between two values:
> 
> py> improve(-0.36787344117144249, -1.0057222396915309)
> 0: w= -1.0057222396915309 err= 2.6521238905750239e-14
> 1: w= -1.0057222396915044 err= -2.6521238905872001e-14
> 2: w= -1.0057222396915309 err= 2.6521238905750239e-14
> 3: w= -1.0057222396915044 err= -2.6521238905872001e-14
> 4: w= -1.0057222396915309 err= 2.6521238905750239e-14
> 5: w= -1.0057222396915044 err= -2.6521238905872001e-14
> [...]
> 32: w= -1.0057222396915309 err= 2.6521238905750239e-14
> 33: w= -1.0057222396915044 err= -2.6521238905872001e-14
> 34: w= -1.0057222396915309 err= 2.6521238905750239e-14
> 35: w= -1.0057222396915044 err= -2.6521238905872001e-14
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "<stdin>", line 19, in improve
> RuntimeError: ('calculation failed to converge', -2.6521238905872001e-14)
> 
> (The correct value for w is approximately -1.00572223991.)
> 
> I know that Newton's method is subject to cycles, but I haven't found any 
> discussion about Halley's method and cycles, nor do I know what the best 
> approach for breaking them would be. None of the papers on calculating
> the Lambert W function that I have found mentions this.
> 
> Does anyone have any advice for solving this?
> 
> 
> 
> -- 
> Steven

I sugest you can use  Taylor's series expansion to speed up w*exp(w)
for negative values of w.



More information about the Python-list mailing list