Finding root/zeros of a function - python

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 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

Related

Infinite loop in binary search algorithm

I'm a newbie in algorithms. I have recently started studying binary search and tryed to implement it on my own. The task is simple: we have an array of integers a and an integer x. If a contains x the result should be its index, otherwise the function should return -1.
Here is the code I have written:
def binary_search(a, x):
l = 0
r = len(a)
while r - l > 0:
m = (l + r) // 2
if a[m] < x:
l = m
else:
r = m
if a[l] == x:
return l
return -1
But this code stucks in infinite cycle on a = [1, 2] and x = 2. I suppose, that I have incorrect cycle condition (probably, should be r - l >= 0), but this solution does not help. Where am I wrong?
Let me do some desk checking. I'll assume a = [1, 2] and we are searching for a 2
So we start with
l = 0
r = 2
Since r - l = 2 > 0, we enter the while-loop.
m = (l + r) / 2 = (0 + 2) / 2 = 1
a[m] = a[1] = 2 == x (hence not less than x)
r = m = 1 (and l remains the same)
Now r - l = 1 - 0 = 1 > 0, so we continue
m = (l + r) / 2 = (0 + 1) / 2 = 0
a[m] = a[0] = 1 < x
l = m = 0 (and r remains the same)
After this iteration both r and l have the same value as before, which then produces an endless loop.
Ashok's answer is a great fix. But I think it'll be educational to do some desk checking on the fixed code and look what improves it.
Basically the problematic situation arises, when l + 1 = r.
Then m will always evaluate to l, a[l] < x and l is set to m again, which doesn't change the situation.
In a larger piece of code it'll make sense to make a table that contains a column for each variable to watch and a column to write down the code line that was evaluated. A column for remarks won't harm either.
As Mani mentioned you are not considering when A[m]==x. Include that case (at that point you've found a so just return m), and once you have that case we can let l=m+1 when we are still below x. Like this:
def binary_search(a, x):
l = 0
r = len(a)
while r - l > 0:
m = (l + r) // 2
if a[m] < x:
l = m + 1
elif a[m]==x:
return m
else:
r = m
if l<len(a) and a[l] == x:
return l
return -1

Python Find the index of value in sequence given the condition

I have this problem but don't know where to start. The only thing I have in my mind is Fibonacci numbers and for loop (but don't know how to apply). It looks like Fibonacci but the first terms are different.
We have a sequence beginning with a then a+b. The 3rd number is
a+b+a, the 4th one is a+b+a+a+b. Which means that a number is
equal to the sum of 2 previous terms. (except the two first terms).
We need to find the index of the number given that the number is
exactly 100k. a and b are picked randomly. The program has to end by print(a,b,index)
So my problem is, I don't know how to choose a and b which can satisfy the 100k condition. For example:
If a == 35k and b == 20k, then the sequence would be like this:
35k, 55k, 90k, 145k and no 100k in the sequence.
So how to deal with this problem? Really appreciate!!
EDIT: this is a correction over my last answer
First write the difference equation according to the described conditions:
f[0] = a
f[1] = a + b
f[2] = f[1] + f[0]
= 2a + b = a + b + a
f[3] = f[2] + f[1] = f[1] + f[0] + f[1]
= 3a + 2b
= a + b + a + a + b
f[4] = f[3] + f[2]
= 3a + 2b + 2a + b = 5a + 3b
f[5] = f[4] + f[3]
= 5a + 3b + 3a + 2b = 8a + 5b
f[6] = f[5] + f[4]
= 8a + 5b + 5a + 3b = 13a + 8b
...
f[n] = f[n-1] + f[n-2]
We can actually simplify this problem if we separate a and b:
f_a[n] = a*(f[n-1] + f[n-2]) with f[0] = 1 and f[1] = 1
f_b[n] = b*(f[n-1] + f[n-2]) with f[0] = 0 and f[1] = 1
Now, if we calculate the solution to the diference equation we should obtain the following assuming that s=sqrt(5) that n \in N (is a natural number):
w1a = ((1+s)/2)ˆ{n+1}
w2a = ((1-s)/2)ˆ{n+1}
w1b = ((1+s)/2)ˆn
w2b = ((1-s)/2)ˆn
f_a[n] = (1/s) * [w1a - w2a] * a
f_b[n] = (1/s) * [w1b - w2b] * b
Simplifying:
l = (1+s)/2
g = (1-s)/2
f[n] = f_a[n] + f_b[n]
= (1/s) * [lˆn(al+b) - gˆn(ag+b)]
You can find more info on how to solve diference equations here: https://www.cl.cam.ac.uk/teaching/2003/Probability/prob07.pdf
You can implement these equations in a Python function to obtain any value of this function.
from math import sqrt
def f(n, a, b):
s = sqrt(5)
l = (1+s)/2
g = (1-s)/2
fn = (1/s) * ((a*l + b) * (l**n) - (a*g + b) * (g**n))
return int(round(fn, 0))
Searching for the index iteratively
You may now find the n which solves this equation for a particular f(n) if you apply the logarithmic function (see the section below). However, if time complexity is not an issue for you, and given that f[n] grows exponentially for n (meaning that you will not need to search much until 100k is reached or surpassed), you may also simply find the n which gives f[n] for a given a and b by doing the following search:
def search_index(a, b, value):
n = 0
while(True):
fn = f(n, a, b)
if fn == value:
return n
elif fn > value:
return -1
else:
n += 1
def brute_search(range_a, range_b, value):
for a in range(range_a + 1):
for b in range(range_b + 1):
if (a == 0) and (b == 0):
a = 1
res = search_index(a, b, value)
if res != -1:
return a, b, res
return -1
brute_search(1000, 1000, 100000)
>>> (80, 565, 12) # a = 80, b = 565 and n = 12
Through this (quite bad) method we find that for a=80 and b=565, n=12 will return f_n = 100k. If you would like to find all possible solutions for a range of values of a and b, you can modify brute_search in the following way:
def brute_search_many_solutions(range_a, range_b, value):
solutions = []
for a in range(range_a + 1):
for b in range(range_b + 1):
if (a == 0) and (b == 0):
a = 1
res = search_index(a, b, value)
if res != -1:
solutions.append((a, b, res))
return solutions
Analytical solution
Transforming the previous diference equation f_n so that now n is a function of a, b and f_n we obtain:
n \aprox log((f_n * s) / (a * l + b)) / log(l)
This result is an approximation, which may guide your search. You can use it in the following way:
def find_n(a, b, value):
s = sqrt(5)
l = (1+s)/2
g = (1-s)/2
return int(round(log(value * s / (a * l + b)) / log(l), 0))
def search(a, b, value):
n = find_n(a, b, value)
sol = f(n, a, b)
if sol == value:
return(a, b, n)
elif sol > value:
for i in range(n-1, 0, -1):
sol = f(i, a, b)
if sol == value:
return(a, b, i)
elif sol < value:
return(-1, 'no solution exits for a={} and b={}'.format(a, b))
else: # this should probably never be reached as find_n should
# provide an upper bound. But I still need to prove it
i = n
while(sol < value):
i += 1
sol = f(i, a, b)
if sol == value:
return(a, b, i)
elif sol > value:
return(-1, 'no solution exits for a={} and b={}'.format(a, b))
search(80, 565, 100000)
>>> (80, 565, 12) # a = 80, b = 565 and n = 12
NOTE: I would have loved to use mathematical notation here with LaTeX, but unfortunately I did not find an easy way to do it... Likely, this question would fit better Stack Exchange, than Stack overflow.

While Loop not functioning properly

I am testing a simple linear diophantine equations code. Here is the code:
a = 3
b = 4
n = 67
i = 0
while True:
if i * a <= n:
if (n - (i * a)) % b == 0:
yy = int((n - (i * a)) / b)
print("{0} x {1} + {2} x {3} = {4}".format(a, i, b, yy, n))
i = i + 1
else:
print("No possible solution!")
break
When the code is run, it is able to find the possible x and y in this equation (which is fine). But, what I can't figure out is why is the print "No Possible solution!" is getting printed together with the answer. The else block is suppose to appear only if a solution is not possible e.g a = 3, b = 4 and n = 2.
Any advice will be appreciated.
print("No possible solution!") is inside the else case so it will execute regardless of whether any solutions were found or not.
Here is one way to fix it where a boolean variable keeps track of whether a solution was found or not and prints the message based on the state of that variable:
a = 3
b = 4
n = 2
i = 0
solution_found = False
while True:
if i * a <= n:
if (n - (i * a)) % b == 0:
yy = int((n - (i * a)) / b)
print("{0} x {1} + {2} x {3} = {4}".format(a, i, b, yy, n))
solution_found = True
i = i + 1
else:
break
if not solution_found:
print("No possible solution!")
Use flag to identify solution is available or not.
a = 3
b = 4
n = 67
i = 0
isSolutionAvailable=False
while True:
if i * a <= n:
if (n - (i * a)) % b == 0:
yy = int((n - (i * a)) / b)
print("{0} x {1} + {2} x {3} = {4}".format(a, i, b, yy, n))
isSolutionAvailable=True
i = i + 1
else:
break
if(not(isSolutionAvailable)):
print("No possible solution!")

distance from root search of tree fails

The tree is as follows:
(1,1)
/ \
(2,1) (1,2)
/ \ / \
(3,1)(2,3) (3,2)(1,3)
and onward
The root is (1,1), all values in the tree are tuples.
Where (x,y) is an element of the tree:
The leftChild will be (x+y,y)
The rightChild will be (x,x+y)
I am building a function that finds the distance from the root (1,1). I cannot build the tree from scratch as it will be too time consuming.
I have found that 1 distance from the tuple being searched for we must subtract the the max with the min. We can work backward.
1 2
(3,2)->(1,2)->(1,1)
(3,2)->(3-2,2) = (1,2)->(1,2-1) = (1,1)
given this is always true:
if x > y:
newtuple = (x-y,y)
distance += 1
else if y > x:
newtuple = (x,y-x)
distance += 1
Yet because possible test cases can go up to even x = 10^50, this is even too slow.
So I have found a formally to find the amount of subtractions of x with y or vice versa to make a x > y change to y < x and vice versa until (x,y) = (1,1).
So X - Y(a certain amount of times, say z) will make x less than y...
X - Y*z = y
find z via algebra... z = (Y-X)/(-Y)
This is my code so far:
from decimal import Decimal
import math
def answer(M,F):
M = int(M)
F = int(F)
i = 0
while True:
if M == 1 and F == 1:
return str(i)
if M > F:
x = math.ceil((F-M)/(-F))
M -= F*x
elif F > M:
x = math.ceil((M-F)/(-M))
F -= M*x
else:
if F == M and F != 1:
return "impossible"
i += x
if M < 1 or F < 1:
return "impossible"
return str(i)
And it's not passing some unknown test cases, yet passes all the test cases I can think of. What test cases could I be failing? Where is my code wrong?
p.s. used Decimal module, just removed from code to make more readible.
Floor division allows for no loss, but maybe -1 in error which I consider for the code below.
def answer(M,F):
M = int(M)
F = int(F)
i = 0
while True:
if M == 1 and F == 1:
return str(i)
if M > F:
x = F-M
x = x//(-F)
if F < M-(F*x):
x += 1
M -= F*x
elif F > M:
x = M-F
x = x//(-M)
if M < F-(M*x):
x += 1
F -= M*x
else:
if F == M and F != 1:
return "impossible"
i += x
if M < 1 or F < 1:
return "impossible"
return str(i)

My bisection code in python doesnt return the root

I am trying to find a good approximation to the root of a function using a bisection algorithm, however, when I run the code it doesnt return the root (c). Here is my code.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-10,10,201)
def f(x):
return np.cos(x)
plt.figure()
plt.plot(x,f(x),label = 'cosx')
plt.xlabel('independent variable x')
plt.ylabel('dependent variable y')
plt.title('')
plt.show()
TOL = 10**-6
a = 0
b = 1
def bisection(a,b,TOL):
c = (a+b)/2.0
while (b-a)/2.0 > TOL:
if f(c)==0:
return c
elif f(a)*f(c)<0:
b = c
else:
a = c
c = (a+b)/2.0
return c
As posted your code don't change the values of c in the loop as it is outside the while loop so the value of a and b never change causing an infinite loop:
def bisection(a,b,TOL):
c = (a+b)/2.0
while (b-a)/2.0 > TOL:
if f(c)==0:
return c
elif f(a)*f(c)<0:
b = c
else:
a = c
c = (a+b)/2.0 # put inside the while
return c
To see the value of c through the loop add a print statement:
def bisection(a, b, TOL):
c = (a + b) / 2.0
while (b - a) / 2.0 > TOL:
if f(c) == 0:
return c
elif f(a) * f(c) < 0:
b = c
else:
a = c
c = (a + b) / 2.0
print ("Current value of c is {}".format(c))
return c

Categories

Resources