While loop will not stop in recursive binary search - python

I am trying to program a recursive binary search algorithm in python. However, I keep running into an infinite while-loop. I am afraid that it must be something simple that I am overlooking, but I cannot find the answer anywhere, most questions about while-loops not terminating do use other conditions and not boolean values.
The algorithm does seem to work, it prints the index of the element that I am searching for, or "Value not found" when the element is not in the list. But the while loop does never terminates, Even though i set found = False after the value has been found/not found. Why is this?
def binarysearch(A, v, x, y):
found = True
while found:
if x < y:
h = (x+y) //2
if A[h] < v:
binarysearch(A, v, h+1, y)
else:
binarysearch(A, v, x, h)
elif A[x] == v:
found = False
print("Element you are looking for is at index {}".format(x))
else:
found = False
print("Value is not in array")
#Code to test the binarysearch algorithm
blist = []
for value in range(0,124):
if value % 2 ==0:
blist.append(value)
print(blist)
binarysearch(blist, 68, 0, len(blist)-1)

The found variable you are modifying with found = False is local to the scope of that particular recursive call to binarysearch. It is not the instance of found you are trying to modify, i.e. the one in the top level of the recursion tree. So although the while-loop in the current scope will terminate, the loops in the scopes above it will not.
Since you already have a mostly complete loop implementation, instead of using recursion on top of it (which is causing the scope-related error) you can just narrow the search range by overwriting x and y.
def binarysearch(A, v, x, y):
found = True
while found:
if x < y:
h = (x+y) //2
if A[h] < v:
x = h+1 // replaced
else:
y = h // replaced
elif A[x] == v:
found = False
print("Element you are looking for is at index {}".format(x))
else:
found = False
print("Value is not in array")

The mistake I made was that I used a while loop ON TOP OF the recursive calls, which basically are two ways of accomplishing the same. For the people interested in the algorithm that uses recursion instead of a while loop to keep it running, I provided a working version of it below.
def binarysearch(A, v, x, y):
if x < y:
h = (x+y) //2
if A[h] < v:
binarysearch(A, v, h+1, y)
else:
binarysearch(A, v, x, h)
elif A[x] == v:
print("Element you are looking for is at index {}".format(x))
else:
print("Value is not in array")

Related

Return second smallest number in a nested list using recursion

I have to return the second smallest number in a python list using recursion, and no loops. What I have done is created a helper function that returns a tuple of the (smallest, second smallest) values in the list, and then I just take the tuple[1] in my second_smallest func.
def s_smallest(L):
if(len(L) == 2):
if (L[0] >= L[1]):
return (L[1],L[0])
else:
return (L[0],L[1])
else:
first_smallest,second_smallest = s_smallest(L[1:])
if L[0] >= first_smallest and L[0] <= second_smallest:
return (first_smallest, L[0])
elif L[0] <= first_smallest:
return (L[0], first_smallest)
else:
return (first_smallest, second_smallest)
This works, but now I need to handle nested lists, so s_smallest([1,2,[3,0]]) should return (0,1). I tried doing this:
if isinstance(L[0],list):
first_smallest,second_smallest = s_smallest(L[0])
else:
first_smallest,second_smallest = s_smallest(L[1:])
to get the first smallest and second smallest values if it is a list, but I get an error saying builtins.TypeError: unorderable types: int() >= list(). How can I fix this problem to deal with nested lists?
I might suggest separating the list unnesting and the min reducing into two separate, well-defined tasks
deepReduce will reduce a list of lists using the specified reducing function
deepMin performs a deepReduce using min
import math # used for math.inf
def min (x,y):
return x if x < y else y
def deepReduce (f, y, xs):
if not xs:
return y
elif isinstance(xs[0], list):
return deepReduce(f, deepReduce(f, y, xs[0]), xs[1:])
else:
return deepReduce(f, f(y, xs[0]), xs[1:])
def deepMin (xs):
return deepReduce (min, math.inf, xs)
data = [1,2,[7,[6,1,3,[0,4,3]],3,4],2,1]
print(deepMin(data))
# 0
Oh, but you said you want the second smallest number. Let's rework that code a little bit. Of course I knew that all along, but answering this question twice allows me to demonstrate the versatility of this specific implementation – Changes in bold
def min2 (xs, y):
# x1 is the smallest, x2 is second smallest
x1, x2 = xs
if (y < x1) and (y < x2):
return (y, x2)
elif y < x2:
return (x1, y)
else:
return (x1, x2)
def deepMin2 (xs):
# notice we change to use tuple of math.inf now
x1, x2 = deepReduce (min2, (math.inf, math.inf), xs)
return x2
data = [1,2,[7,[6,1,3,[0,4,3]],3,4],2,1]
print(deepMin2(data))
# 1
I should point out that we didn't have to touch deepReduce at all, which is the point – we should be able to do any arbitrary deep operation on our nested list without having to statically code that behaviour into our function.
Now you can write whatever deep reducer you want and call it with deepReduce
Full solution
Using nothing but functools.reduce, no loops, to handle lists of arbitrary nesting:
import functools
def helper(acc, x):
if type(x) is list:
return functools.reduce(lambda acc, x: helper(acc, x), x, acc)
else:
if x < acc[0]:
return (x, acc[0])
elif x < acc[1]:
return (acc[0], x)
else:
return (acc[0], acc[1])
def second_smallest(l):
if len(l) < 2:
return None
else:
if l[0] <= l[1]:
return functools.reduce(lambda acc, x: helper(acc, x), l[2:], (l[0], l[1]))
else:
return functools.reduce(lambda acc, x: helper(acc, x), l[2:], (l[1], l[0]))
>>> second_smallest([1,2,[0,3,[-1,-2]]])
(-2, -1)

How to use recursion to produce the sum of powers?

I am trying to write a function using only recursion (and no built-in functions) that consumes two numbers, x and y and produces the sum
1 + x + x^2 + ... + x^(y-1) + x^y
Note that I am looking for a way to do this without using for/while loops because I have not learned them yet. So far, I have the following function:
def power_addition (x, y):
i = 0
if i < y:
i = i+1
return x**i + power_addition (x, y)
else:
return x**i
As far as I can tell, the code breaks for one particular reason.
def sum_of_powers(x,y):
if y<=0: return 1
return x**y + sum_of_powers(x,y-1)

How to stop a loop?

def sum_div(x, y):
for k in range(x,y+1):
for z in range(x,y+1):
sx = 0
sy = 0
for i in range(1, k+1):
if k % i == 0:
sx += i
for j in range(1, z+1):
if z % j == 0:
sy += j
if sx == sy and k!= z:
print "(", k ,",", z, ")"
x = input("Dati x : ")
y = input("Dati y : ")
sum_div(x, y)
How do I stop the looping if the value of z == y?
The loops print a pair of numbers in a range from x to y, but when it hit the y value the loop prints a reverse pair of numbers that I don't need it to.
The break command will break out of the loop. So a line like this:
if (z == y):
break
should do what you want.
What you're think you are asking for is the break command, but what you're actually looking for is removal of duplication.
Your program lacks some clarity. For instance:
for i in range(1, k+1):
if k % i == 0:
sx += i
for j in range(1, z+1):
if z % j == 0:
sy += j
These two things are doing essentially the same thing, which can be written more cleanly with a list comprehension (in the REPL):
>>> def get_divisors(r: int) -> list:
... return [i if r % i == 0 else 0 for i in range(1, r+1)]
...
...
>>> get_divisors(4)
>>> [1, 2, 0, 4]
>>> sum(get_divisors(4))
>>> 7
Your line:
while y:
... will infinitely loop if you find a match. You should just remove it. while y means "while y is true", and any value there will evaluate as true.
This reduces your program to the following:
def get_divisors(r: int) -> list:
return [i if r % i == 0 else 0 for i in range(1, r+1)]
def sum_div(x, y):
for k in range(x,y+1):
sum_of_x_divisors = sum(get_divisors(k)) # Note this is moved here to avoid repeating work.
for z in range(x,y+1):
sum_of_y_divisors = sum(get_divisors(z))
if sum_of_x_divisors == sum_of_y_divisors and k!= z:
print("({},{})".format(k, z))
Testing this in the REPL it seems correct based on the logic of the code:
>>> sum_div(9,15)
(14,15)
(15,14)
>>> sum_div(21, 35)
(21,31)
(31,21)
(33,35)
(35,33)
But it's possible that for sum_div(9,15) you want only one of (14,15) and (15,14). However, this has nothing to do with breaking your loop, but the fact that what you're attempting to do has two valid values when k and z don't equal each other. This is demonstrated by the second test case, where (33,35) is a repeated value, but if you broke the for loop on (21,31) you would not get that second set of values.
One way we can account for this is by reordering when work is done:
def sum_div(x, y):
result_set = set() # Sets cannot have duplicate values
for k in range(x,y+1):
sum_of_x_divisors = sum(get_divisors(k))
for z in range(x,y+1):
sum_of_y_divisors = sum(get_divisors(z))
if sum_of_x_divisors == sum_of_y_divisors and k!= z:
result_set.add(tuple(sorted((k,z)))) # compile the result set by sorting it and casting to a tuple, so duplicates are implicitly removed.
for k, z in result_set: # Print result set after it's been compiled
print("({},{})".format(k, z))
And we see a correct result:
>>> sum_div(9,15)
(14,15)
>>> sum_div(21,35)
(21,31)
(33,35)
Or, the test case you provided in comments. Note the lack of duplicates:
>>> sum_div(10,25)
(16,25)
(14,15)
(15,23)
(10,17)
(14,23)
Some takeaways:
Break out functions that are doing the same thing so you can reason more easily about it.
Name your variables in a human-readable format so that we, the readers of your code (which includes you) understands what is going on.
Don't use loops unless you're actually looping over something. for, while, etc. only need to be used if you're planning on going over a list of things.
When asking questions, be sure to always include test input, expected output and what you're actually getting back.
The current best-practice for printing strings is to use the .format() function, to make it really clear what you're printing.

sum variable in a range (python)

This was the question:
Write a function called sum_range that accepts 2 integer values as
parameters and returns the sum of all the integers between the two
values, including the first and last values. The parameters may be in
any order (i.e. the second parameter may be smaller than the first).
For example:
result = sum_range(1, 1)
print(result) 1
result = sum_range(2, 4) print(result) 9
result = sum_range(3, 2)
print(result) 5
my codes are as below, I dont know where it went wrong
but when I test the codes, it returned 'none' when (2,4) (3,2) were entered
def sum_range(x,y):
if x == y:
return x
if x<y:
sum(range(x,y))
return
if x>y:
sum(range(y,x))
return
You could do better (at least I think), here is my code for that:
def sum_range(a, b):
return sum(range(min(a,b),max(a,b)+1))
You were very close but forgot to return the actual value from the calculations. If you just type "return", you will return None and not the result from the sum.
You also did not include the last number in the range in the sum. See corrected code below:
def sum_range(x, y):
if x == y:
return x
if x < y:
return sum(range(x, y+1))
if x > y:
return sum(range(y, x+1))
You need to return the sum which you are not doing in the x<y and x>y cases. You should
return sum(range(x,y)) or return sum(range(y,x)) as appropriate.
Note also that there is a bug in your range() expressions - "including the first and last values". Hint: What does range(1,3) output?
def sum_range(x,y):
if x == y:
return x
elif x < y:
s = 0
for i in range(x,y):
s += x+(x+1)
return s
elif x > y:
s = 0
for i in range(y,x):
s += y+(y+1)
return s
This is done without using sum() function.

Basics of Simulated Annealing in Python [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I have to use simulated annealing for a certain optimization problem. To get a 'feel' of the technique, I wrote a small python code and tried to run it. However, it doesn't seem to be giving satisfactory results.
import random;
import math;
from math import *;
LIMIT=100000;
def update_temperature(T,k):
T1=T/log(k+1);
# print "temp now is " + str(T1);
return T1;
def get_neighbors(i,l):
if(l>1):
if(0<=i and i<l):
if(i==0):
return [1];
if(i==l-1):
return [l-2];
return [i-1,i+1];
return [];
def make_move(x,A,T):
nhbs=get_neighbors(x,len(A));
nhb=nhbs[random.choice(range(0,len(nhbs)))];
delta=A[nhb]-A[x];
if(delta < 0):
return nhb;
else:
r=random.random();
if(r <= (e**(-1*delta)/(T*1.0))):
return nhb;
return x;
def simulated_annealing(A):
l=len(A);
init_pos=random.choice(xrange(0,l));
T=10000**30;
k=1;
x_best=init_pos;
x=x_best;
while(T>0.0000001 ):
x=make_move(x,A,T);
if(A[x] < A[x_best]):
x_best=x;
T=update_temperature(T,k);
k+=1;
return [x,x_best,init_pos];
def isminima_local(p,A):
l=len(A);
if(l==1 and p==0):
return True;
if(l>1):
if(p==0):
if(A[0] <=A[1]):
return True;
if(p==l-1):
if(A[p-1] >=A[p]):
return True;
if(0<=p and p<l and A[p-1]>=A[p] and A[p]<=A[p+1]):
return True;
return False;
def func(x):
F=sin(x);
return F;
def initialize(l):
A=[0]*l;
for i in xrange(0,l):
A[i]=func(i);
return A;
def main():
A=initialize(LIMIT);
local_minima=[];
for i in xrange(0,LIMIT):
if( isminima_local(i,A)):
local_minima.append([i,A[i]]);
sols=simulated_annealing(A);
m,p=A[0],0;
for i in xrange(1,LIMIT):
if(m>A[i]):
m=A[i];
p=i;
print "Global Minima at \n";
print p,m;
print "After annealing\n";
print "Solution is " + str(sols[0]) + " " + str(A[sols[0]]);
print "Best Solution is " + str(sols[1]) + " " + str(A[sols[1]]);
print "Start Solution is " + str(sols[2]) + " " + str(A[sols[2]]);
for i in xrange(0,len(local_minima)):
if([sols[0],A[sols[0]]]==local_minima[i]):
print "Solution in local Minima";
if([sols[1],A[sols[1]]]==local_minima[i]):
print "Best Solution in local Minima";
for i in local_minima:
print i;
main();
I am unable to understand where I am going wrong. Is there something wrong with the implementation or is there something wrong in my understanding about simulated annealing ? Please point out the error..
My rough idea about SA:
Pick a random neighbor
If neighbor improves your condition, move there,
Else, move there with certain probability.
The probability is such that initially bad moves are 'allowed' but they are 'prohibited' later on. Finally you will converge to your solution.
I have found the set of local minima and global minima using brute force. Then I run SA. I was expecting that SA will atleast converge to a local minima but that doesn't seem to be the case always. Also, I am not sure if at every step I choose a neighbor randomly and then try to move or I choose the best neighbor ( even if none of the neighbors improve my condition) and then try to move there.
For the most part, your code seems to work well. The main reason that it's slow to converge is that you only look at the two neighbors on either side of your current point: if you expand your search to include any point in A, or even just a wider neighborhood around your current point, you'll be able to move around the search space much more quickly.
Another trick with simulated annealing is determining how to adjust the temperature. You started with a very high temperature, where basically the optimizer would always move to the neighbor, no matter what the difference in the objective function value between the two points. This kind of random movement doesn't get you to a better point on average. The trick is finding a low enough starting temperature value such that the optimizer will move to better points significantly more often than it moves to worse points, but at the same time having a starting temperature that is high enough to allow the optimizer to explore the search space. As I mentioned in my first point, if the neighborhood that you select points from is too limited, then you'll never be able to properly explore the search space even if you have a good temperature schedule.
Your original code was somewhat hard to read, both because you used a lot of conventions that Python programmers try to avoid (e.g., semicolons at ends of lines), and because you did a few things that programmers in general try to avoid (e.g., using lowercase L as a variable name, which looks very similar to the numeral 1). I rewrote your code to make it both more readable and more Pythonic (with the help of autopep8). Check out the pep8 standard for more information.
In make_move, my rewrite picks one random neighbor from across the whole search space. You can try rewriting it to look in an expanded local neighborhood of the current point, if you're interested in seeing how well that works (something between what you had done above and what I've done here).
import random
import math
LIMIT = 100000
def update_temperature(T, k):
return T - 0.001
def get_neighbors(i, L):
assert L > 1 and i >= 0 and i < L
if i == 0:
return [1]
elif i == L - 1:
return [L - 2]
else:
return [i - 1, i + 1]
def make_move(x, A, T):
# nhbs = get_neighbors(x, len(A))
# nhb = nhbs[random.choice(range(0, len(nhbs)))]
nhb = random.choice(xrange(0, len(A))) # choose from all points
delta = A[nhb] - A[x]
if delta < 0:
return nhb
else:
p = math.exp(-delta / T)
return nhb if random.random() < p else x
def simulated_annealing(A):
L = len(A)
x0 = random.choice(xrange(0, L))
T = 1.
k = 1
x = x0
x_best = x0
while T > 1e-3:
x = make_move(x, A, T)
if(A[x] < A[x_best]):
x_best = x
T = update_temperature(T, k)
k += 1
print "iterations:", k
return x, x_best, x0
def isminima_local(p, A):
return all(A[p] < A[i] for i in get_neighbors(p, len(A)))
def func(x):
return math.sin((2 * math.pi / LIMIT) * x) + 0.001 * random.random()
def initialize(L):
return map(func, xrange(0, L))
def main():
A = initialize(LIMIT)
local_minima = []
for i in xrange(0, LIMIT):
if(isminima_local(i, A)):
local_minima.append([i, A[i]])
x = 0
y = A[x]
for xi, yi in enumerate(A):
if yi < y:
x = xi
y = yi
global_minumum = x
print "number of local minima: %d" % (len(local_minima))
print "global minimum #%d = %0.3f" % (global_minumum, A[global_minumum])
x, x_best, x0 = simulated_annealing(A)
print "Solution is #%d = %0.3f" % (x, A[x])
print "Best solution is #%d = %0.3f" % (x_best, A[x_best])
print "Start solution is #%d = %0.3f" % (x0, A[x0])
main()

Categories

Resources