Count the number of opertations inside a recursive algorithm in python - python

I'm implementing the dichotomic search algorithm in python, in a second version of the function I have to (in addition of returning true or false according to the presence or not of the element) count the number of operations (comparisons) done by the algorithm depending on the length of the list I'm working with and return it.
However, my function is recursive and naturally I'll have to initialize a counter variable (which will be incremented at every operation) to zero. the issue is that this variable will take the zero value at every recursive call and thus, it will not give me the correct value. I thought of a global variable but I don't know how to use it.
Here is the code of my function :
def trouve(T, x) :
if len(T) == 0 :
return False
mid = len(T) // 2
if T[mid] == x :
return True
if len(T) == 1 and T[0] != x :
return False
else :
if x > T[mid] :
return trouve(T[mid:], x)
else :
return trouve(T[:mid], x)

Normally, you would count the comparisons of data only, so not the conditions where you compare the length of the input list.
You could use a third argument to accumulate the count, and then let the function return a tuple of both the success and the count:
def trouve(T, x, c = 0):
if len(T) == 0:
return (False, c) # len() comparisons do not count
mid = len(T) // 2
if T[mid] == x:
return (True, c+1)
if len(T) == 1: # you don't need to compare x again here!
return (False, c+1)
# you don't need `else` here
if x > T[mid]:
return trouve(T[mid:], x, c+2)
# you don't need `else` here
return trouve(T[:mid], x, c+2)
print (trouve([1,3,8,13,14,15,20], 14))
Note that you can optimise a bit:
def trouve(T, x, c = 0):
if len(T) == 0:
return (False, c)
mid = len(T) // 2
# you don't need the `len(T) == 1` case, as it can be
# treated in the recursive call. See change below:
if x > T[mid]:
return trouve(T[mid+1:], x, c+1) # exclude mid itself
# Move equality test below greater-then test, since the
# equality has little chance of being true:
if T[mid] == x:
return (True, c+2)
return trouve(T[:mid], x, c+2)
print (trouve([1,3,8,13,14,15,20], 14))
... although for the example I gave, the count is still the same in this version.

If you want to go the global variable route (since you mentioned it), this is how you would do it.
trouve_count = 0
def trouve(T, x) :
global trouve_count
# Increment trouve_count like this when necessary:
trouve_count += 1
# ...
Be careful using these in larger programs, as you may accidentally use the same global name twice, causing problems.

Related

Python - Returning single value from 2-argument recursive call

So I'm trying to make a simple program that checks whether or not there's 2 8s in a row in a number. I know I can simply do
def double_eights(n):
return '88' in str(n)
but I wanted to try writing the recursive method below
def double_eights(n, counter=0):
#base cases
if counter == 2:
return True
if n == 0:
return False
#recursive calls
if n % 10 == 8:
return(double_eights(n//10, counter + 1))
else:
return(double_eights(n//10), 0)
However, while this returns the right results, due to the way the arguments are passed in, sometimes it returns a tuple with a 0 for all the non-8 numbers that it passes. For example:
>>> double_eights(2882)
(True, 0)
>>> double_eights(80808080)
((((False, 0), 0), 0), 0)
Is there a way for the function to return only the True or False value without needing to parse the returned tuples?
Thanks in advance!
The problem is in the last line of your code. You return result of double_eights(n//10) call and 0, so these values are packed in tuple.
You can change last line of your code to
return(double_eights(n//10, 0))
to specify counter value, but it is not necessery, because counter has default value
def double_eights(n, counter=0):
so you don't need to specify it. Final code may look like:
def double_eights(n, counter=0):
#base cases
if counter == 2:
return True
if n == 0:
return False
#recursive calls
if n % 10 == 8:
return(double_eights(n//10, counter + 1))
else:
return(double_eights(n//10))
Output:
double_eights(2882)
True
double_eights(80808080)
False

why is function returning a different thing when called alone VS when called twice

so i have a recursive function that i call 2 times with different arguments but the second time i call it, it doesnt give the correct result, it only gives the correct result when i call it the first time not the second time. Maybe a memoy or cache issue ?
def m(n):
s= [int(char) for char in str(n)]
product = 1
for x in s:
product *= x
return product
i = 0
def persistence(n):
global i
if len(str(n)) == 1:
return i
else:
j = m(n)
i+=1
s = persistence(j)
return s
print(persistence(39))
print(persistence(4)) #returns 3 when called with the top one but 0 when called alone
You have a condiction len(str(n)) in your function persistence, so that if n is a number between 0 and 9, you return global i.
When you call print(persistence(4)) alone, your function return global i (equal to 0).
When you call print(persistence(39)) first, and then print(persistence(4)), the first call set global i to 3, and the second call juste return the value value of global i, that as just been set to 3.
I don't really get what you want to do, but maybe your problem comes from the usage of global.
The problem is that you are using i as global variable. When you execute the method again, your i
def m(n):
s= [int(char) for char in str(n)]
product = 1
for x in s:
product *= x
return product
def persistence_recursive(n, i):
if len(str(n)) == 1:
return i
else:
j = m(n)
i+=1
s = persistence_recursive(j, i)
return s
def persistence(n):
return persistence_recursive(n, 0)
print(persistence(39)) # returns 3
print(persistence(4)) # returns 0

how to Find the largest element in a list using recursion python?

i = 0
def find_max(seq):
if i == len(seq) - 1:
return seq[0]
else:
first = seq[i]
i = i + 1
max_of_rest = find_max(seq)
return max(first, max_of_rest)
I do not know what is wrong with this function? It is a infinite loop.
Please check out the following solution and follow comments:
def find_biggest(_list, max_element, first_run):
"""
_list is a list of floats or integers or both,
max element is used to store max value,
first run checks if _list is not empty
"""
if first_run and not _list: # check if _list is not empty
raise ValueError("_list should have float or integer values inside")
first_run = False
if not _list: # exit from recursion, we checked all elements
return max_element
element = _list.pop() # take one element
if type(element) not in (int, float,): # check element type
raise TypeError("_list should contain only int or float values")
if element >= max_element: # check if it is more than max
max_element = element
return find_biggest(_list, max_element, first_run) # next step of recursion
if __name__ == "__main__":
# test
print(find_biggest([-1, 4, 2, 3, 1, 0, 10, 3, 1, 7], 0, True))
# print(find_biggest([], 0, True)) # empty case
# print(find_biggest([-1, 4, 2, 3, "1", 0, 10, 3, 1, 7], 0, True)) # string in list
You can check this:
def find_max(seq):
if len(seq) == 1:
return seq[0]
else:
if seq[0] > seq[1]:
seq.pop(1)
else:
seq.pop(0)
return find_max(seq)
Your code has a lot of indentation issues, that may skip execution of some lines.
Your code should look like this:
i = 0
def find_max(seq):
global i
if i == len(seq) - 1:
return seq[0]
else:
first = seq[i]
i = i + 1
max_of_rest = find_max(seq)
return max(first, max_of_rest)
You missed the global, and thus there is no definition of i inside the function.
Your code contains an IndentationError and does not reduce its data on recursive calls - hence data never getting shorter - hence never ending recursion:
def find_max(seq):
if i == len(seq) - 1: # fixed indentation here and below
return seq[0]
else:
first = seq[i]
i = i + 1
max_of_rest = find_max(seq) # never reducing your data size, hence endless
return max(first, max_of_rest)
This would be a fixed recursive solution:
def find_max(seq):
if not seq:
return None # or raise ValueError if you want find_max([]) to crash
if len(seq) == 1:
return seq[0]
else:
return max(seq[0], find_max(seq[1:]))
The problem is inheritently bad for recursive solutions, it is far better to solve it linearly (no max(..) calls needed):
def find_max_lin(seq):
if not seq:
return None
m = seq[0]
for value in seq[1:]:
m = m if value < m else value
return m
or even better simply use the built in max(sequence):
def find_max_builtin(seq):
# no need to create your own function for that though
return max(seq)
See ternary operator for an explanation of what m = m if value < m else value does.
You are using uneeded i variable, in recursion you have base case (your first if), and recursion case, which in this case would be accessing first and second element of your list. As you already checked that the list seq has more than 1 element, you can confidently access positions 0 and 1 of the list.
In your specific case, you are not really using recursion because you never reduce your case, but instead you increment an i variable, whilst recursion is based on always calling the same function with a "simpler" or reduced problem.
With that in mind, several things can be improved in your solution.
i = 0 # Not adviced
def find_max(seq):
# Here you want to check length, making it
# depend on i = problems
if i == len(seq) - 1:
return seq[0]
else:
first = seq[i] # Remove all references to i
i = i + 1 # Remove
# Here you want to call the function with the list
# except the element you know is lower, making the problem
# smaller until you get to the base case
# Instead you are calling find_max with the same
# sequence you had (infinite loop) and returning a
# totally different result.
max_of_rest = find_max(seq)
return max(first, max_of_rest)
A complete solution based on your code would look like this
def find_max(seq):
if len(seq) == 0:
return None
if len(seq) <= 1:
return seq[0]
else:
current_max = max(seq[0], seq[1])
reduced_seq = [current_max] + seq[2:]
return find_max(reduced_seq)
i = 0
def find_max(seq):
global i
if i == len(seq) :
return seq[0]
else:
first = seq[i]
i = i + 1
max_of_rest = find_max(seq)
return max(first, max_of_rest)
print(find_max([-10,2,4,-5]))
thank me later

While Loop true and false for approximation of roots

I'm a bit new to programming, and I'm trying to create a root-approximating code. Namely, I'm doing something similar to Newton's method in calculus. The idea is, I'm going to input in a big value, subtract until I know I've passed the root, and then add a smaller quantity until I've passed the root, and iterate until I'm in some comfortable error region.
Here's some pseudo code:
def approx(a,b,i):
while ((1/2)**i) >= (1/2)**10:
while (another function is true):
modify values, record root = r
while (the same function above is false):
modify values, record root = r
return approx(a,b,i+1)
return(a,b,r)
This does not seem to work in Python, so I was wondering if anyone could point me in the correct direction.
Edit: included my actual code:
from fractions import *
from math import sqrt
from math import fabs
def pweight(c,d):
if d > c:
return pweight(d,c)
else:
return [c+d,c,d]
def eweight(a,b):
if a == b:
return [a]
elif b > a:
return eweight(b,a)
else:
return [b] + eweight(a-b,b)
def weight(a,b,c,d):
if a*b/2 > c*d:
print("No Embedding Exists")
return (False)
else:
return (True, [c+d]+sorted((pweight(c,d) + eweight(a,b))[1:], reverse=True))
def wgt(a,b,c,d):
return ([c+d]+sorted((pweight(c,d) + eweight(a,b))[1:], reverse=True))
def red(a,i,k):
d=a[0]-a[1]-a[2]-a[3]
if any(item < 0 for item in a[1:]):
# print ("No Embedding Exists")
return (False, i)
elif d >= 0:
# print ("Embedding Exists! How many iterations?")
# print(i)
return (True, i)
elif d<0:
a=[a[0]+d,a[1]+d,a[2]+d,a[3]+d]+a[4:]
a=[a[0]]+sorted(a[1:],reverse=True)
k.append(a)
i=i+1
return red(a,i,k)
def works(a,b):
L = sqrt(a/(2*b))
w = weight(1,a,L,L*b)
return w[0] and red(w[1],0,[])
def inf(a,b,i):
while ((1/2)**(i+1)) >= (1/2)**(10)):
while works(a,b):
a = a - (1/2)**i
L = sqrt(a/(2*b))
while not works(a,b):
a = a + (1/2)**(i+1)
L = sqrt(a/(2*b))
return inf(a,b,i+1)
return (a,b,L)
I want to input in "inf(9,1,0)" and have this code return something close to (255/32,1,sqrt(255/64)). The main problem is the "while works(a,b):" and "while not works(a,b):" in the function "inf(a,b,i)." I want the function to alternate between the "while works" and "while not works" until i=9.
Any sort of general idea would be appreciated (namely, how do you do some sort of alternating function within a while loop).
If you want to alternate between them, don't put them each in their own while loops, put
while i < 9:
if works(a, b):
do something
if not works(a, b):
do something else
And whatever you test in your while conditions needs to be something that changes somewhere in the loop. Otherwise you'll get an infinite loop.

Python, check if sets check out

I have made the function, but I need to make a guess so it will run through the function and check if it fits, if not, start over again with new numbers.
If I find a set that works, the loop should break, the problem is that I am new to python and math programming.
def checkStuff(X):
ok = True
#i.
if(min(X) <= 0):
ok = False
#ii.A
A = set()
for x in X:
A.add(x % 2)
#ii.B
B = set()
for y in X:
B.add(y**2)
#ii.C
C = set()
for z in X & B:
C.add(z**0.5)
#ii.D
D = set()
for w in C:
D.add(w**2)
#iii.
if(len(X)<=0):
ok = False
#iv.
if(len(X) not in X):
ok = False
#v.
if len(A) in X:
ok = False
#vi.
if sum(X) not in B:
ok = False
#vii.
if sum(X&B) in B:
ok = False
#viii.
if sum(C.union(D)) not in X:
ok = False
return ok
without giving you the exact code, try looking at the while loop and the random function
Your function can be simplified and optimized, returning as soon as possible, avoiding further computations... for compactness I used set comprehensions instead of your loops
def checkStuff(X):
if(min(X) <= 0): return False
if(len(X)<=0): return False
if(len(X) not in X): return False
A = {x % 2 for x in X}
if len(A) in X: return False
B = {x**2 for x in X}
if sum(X) not in B: return False
if sum(X&B) in B: return False
C = {xb**0.5 for xb in X&B}
D = {c**2 for c in C}
if sum(C.union(D)) not in X: return False
return True
Assuming that you have a function that returns a list of trial sets or, possibly better, yields a new trial set for each loop, and that you want to use ONLY the first X that matches your conditions, then you can write your stuff like this
for X in generate_trial_sets():
if checkStuff(X):
do_stuff(X)
break
else:
print("No X was generated matching the criteria")
...
Note that the else clause is aligned correctly, because Python has a for ... else .. control flow construct.
Blind Attempt at a generate_trial_sets Function
Given that each X is a set of numbers (integers? reals? complex numbers? who knows? you, but you didn't care to tell...) and that we don't know how many numbers you want in the set, and also that you want to stop the iteration somehow, I'd write
def generate_trial_sets(nmin=1, nmax=5,
xmin=0.0, xmax=10.0, iterations=10):
from random import randint
for _ in range(iterations):
n = randint(nmin,nmax+1)
x = {n}
for i in range(1,n):
x.add((xmax-xmin)*random()+xmin)
yield x
When you call it like
for X in generate_trial_sets():
without modifying the default args, you get back 10 sets of length comprised between 1 and 5, with real values comprised between 0 and 10 (one of the values is equal to the length, so one of your tests is automatically fulfilled).
To use different parameters, specify them at the invocation:
for X in generate_trial_sets(nmin=6,nmax=6,xmax=100.0,iterations=200):
This is not a solution of your problem but if you understand the logic you'll get started in the right direction or, at least, I hope so...

Categories