I am trying to find a root of a function by using the bisection method stating that :
if f(a)*f(b) < 0 then a root exists,
then you repeat with f(a)*f(c)<0 where c = (a+b)/2
but im not sure how to fix the code so it works properly. This is my code but its not开发者_StackOverflow working properly
from scipy import *
from numpy import *
def rootmethod(f, a, b, tol):
x = a
fa = sign(eval(f))
x = b
fb = sign(eval(f))
c = a + b
iterations = 0
if fa == 0:
return a
if fb == 0:
return b
calls = 0
fx = 1
while fx != 0:
iterations = iterations + 1
c *= 0.5
x = a + c
fc = sign(eval(f))
calls = calls + 1
if fc*fa >= 0:
x = a
fx = sign(eval(f))
if fc == 0 or abs(sign(fc)) < eps:
fx = sign(eval(f))
return x, iterations, calls
print rootmethod("(x-1)**3 - 1", 1, 3, 10*e-15)
New edit.. but still doesnt work
if fa*fb < 0:
while fx != 0:
iterations = iterations + 1
c = (a + b)/2.0
x = c
fc = sign(eval(f))
calls = calls + 1
if fc*fa >= 0:
x = c
fx = sign(eval(f))
if fc == 0 or abs(sign(fc)) < tol:
fx = sign(eval(f))
return x, iterations, calls
Edit: Changed c=(a+b)*2 to c=(a+b)/2 in the description of the method.
Frankly your code was a bit of a mess. Here's some that works. Read the comments in the loop. (BTW the solution to your given function is 2, not 3.75)
from scipy import *
from numpy import *
def rootmethod(f, a, b, tol):
x = a
fa = sign(eval(f))
x = b
fb = sign(eval(f))
c = a + b
iterations = 0
if fa == 0:
return a
if fb == 0:
return b
calls = 0
fx = 1
while 1:
x = (a + b)/2
fx = eval(f)
if abs(fx) < tol:
return x
# Switch to new points.
# We have to replace either a or b, whichever one will
# provide us with a negative
old = b # backup variable
b = (a + b)/2.0
x = a
fa = eval(f)
x = b
fb = eval(f)
# If we replace a when we should have replaced b, replace a instead
if fa*fb > 0:
b = old
a = (a + b)/2.0
print rootmethod("(x-1)**3 - 1", 1, 3, 0.01)
I believe your loop should be something like this (in pseudocode, and leaving out some checking):
before loop:
a is lower bound
b is upper bound
Establish that f(a) * f(b) is < 0
while True:
c = (a+b)/2
if f(c) is close enough to 0:
return c
if f(a) * f(c) > 0:
a = c
else
b = c
In other words, if the midpoint isn't the answer, then make it one of the new endpoints depending on its sign.
I think your one problem is with:
x = a + c
Since c = (a + b)*.5
, you don't need to add in a
here...
Update
You don't seem to check if fa * fb < 0
to start, and I also don't see where you're narrowing your bounds: you should either reassign a
or b
to c
in your loop and then recalculate c
.
Code It's been a while since I played with python last, so take it with a grain of salt ^_^
x = a
fa = sign(eval(f))
x = b
fb = sign(eval(f))
iterations = 0
if fa == 0:
return a
if fb == 0:
return b
calls = 0
fx = 1
while fa != fb:
iterations += 1
c = (a + b)/2.0
x = c
fc = eval(f)
calls += 1
if fc == 0 or abs(fc) < tol:
#fx = fc not needed since we return and don't use fx
return x, iterations, calls
fc = sign(fc)
if fc != fa:
b = c
fb = fc
else
a = c
fa = fc
#error because no zero is expected to be found
Please note that the code has a simple deficiency caused by round-off errors
a=0.015707963267948963
b=0.015707963267948967
c=(a+b)*.5
c
is becoming b
again (check it out!).
You can end up in infinite loop
in case of very small tolerance like 1e-16.
def FindRoot( fun, a, b, tol = 1e-16 ):
a = float(a)
b = float(b)
assert(sign(fun(a)) != sign(fun(b)))
c = (a+b)/2
while math.fabs(fun( c )) > tol:
if a == c or b == c:
break
if sign(fun(c)) == sign(fun(b)):
b = c
else:
a = c
c = (a+b)/2
return c
Now, to call the eval over and over again is not very efficient. Here is what you can do instead
expr = "(x-1.0)**3.0 - 1.0"
fn = eval( "lambda x: " + expr )
print FindRoot( fn, 1, 3 )
Or you can put the eval and lambda definition inside the FindRoot.
Was it helpful?
Reson
精彩评论