Reducing loop/recursion time - python

Hopefully I can explain this correctly without having to paste a whole wall of code in.
I have a list [0:100]. I need to find the lowest sufficient value in the list that will return a True in another function.
The trick is that I need this to be done fast! so far some of the data has taken over 65 seconds to process, due to the latter function iterating over the specific index in the list over 1000 times.
def search(list,grid):
print(list)
if len(list) <= 2:
return list[0]
else:
p = len(list)//2
print(p)
print(list[p])
if simulation(grid,list[p]) == 'lose':
return search(list[p:len(list)-1],grid)
elif simulation (grid,list[p]) == 'win':
return search(list[0:p],grid)
For a certain input I need the function to return a result in less than 10 seconds. Is there anyway that I can precisely find the value I'm looking for without having to pass every single values from 0 to 100 to the function that relies on the list?

It may not be sufficient, to reach your goal, but one obvious improvement is to avoid recalculating the simulation for the same values, in case it is not a lose.
Removing all print output will speed things up too.
def search(seq, grid):
if len(seq) <= 2:
return seq[0]
p = len(seq)//2
sim_result = simulation(grid, seq[p])
if sim_result == 'lose':
return search(seq[p: len(seq)-1], grid)
elif sim_result == 'win':
return search(seq[0: p], grid)
You will probably have to look at how the simulation is run to improve further.

Related

Error: variable not storing modified value on recursion

I am finding count of all the ways a target is reached. In base case, i am updating the value but when returning, it is taking the initial value only. How to change it to updated value, Kindly help me making changes in this code only and let me know how can i store so that it can return the modified value.
Input list:[1,2,3]
target:3
Output: 2 as [1,2] and [3] will make it 3
def counter(ind,grid,target,count):
if target==0: #if target becomes 0(achieved)
count+=1
return count
if ind==0: #if ind=0 is reached and target=value at that index(achieved)
if target==grid[ind]:
count+=1
return count
else:
return
nottake=counter(ind-1,grid,target,count) #not taking the index's value
take=0
if target-grid[ind]>=0: #only if value at index is smaller that target
take=counter(ind-1,grid,target-grid[ind],count) #taking the index's value
return count
grid=[1,2,3]
target=3
ind=len(grid)-1
print(counter(ind,grid,target,0)) #output should be 2 but i am getting 0
For starters, please format your code with Black. It's difficult to understand code that's scrunched together. Also, use default values to avoid burdening the caller with fussy indices and extra parameters.
This approach exhibits a common mistake with recursion: trying to pass the result downward through the recursive calls as well as upward. Just pass the result upward only, and pass parameters/state downward.
Doing so gives:
from functools import cache
#cache
def count_ways(available_numbers, target, i=0):
if target == 0:
return 1
elif target < 0:
return 0
elif i >= len(available_numbers):
return 0
take = count_ways(available_numbers, target - available_numbers[i], i + 1)
dont_take = count_ways(available_numbers, target, i + 1)
return take + dont_take
if __name__ == "__main__":
print(count_ways(available_numbers=(1, 2, 2, 1, 3, 4) * 70, target=7))
This is clearly exponential since each recursive call spawns 2 child calls. But adding a cache (formerly lru_cache(maxsize=None) prior to CPython 3.9) avoids repeated calls, giving a linear time complexity as long as the list fits within the stack size. Use a bottom-up dynamic programming approach if it doesn't

How to keep track the amount of even integers when looking through randomized list using recursion

Sorta newbie here. So in trying to wrap my head around using recursive functions I wanted to try to make a program that:
1: Generates a list containing 10 random integers ranging from 0 - 20
2: Using a recursive function goes trough the list and finds out what elements of the list are even integers
3: Prints out only the aformentioned even numbers
Where I have gotten stuck is in how to print out the result. I can't seem to figure out what value i want to put inside the function when calling it ( F(?) )
I tried to integrate a counter that kept track on how many times the program found a even number but it always resulted in an error that the variable is not defined no matter how hard I tried to make it global.
How could I go about this? Am I totally in the wrong?
import random
numL = []
for i in range(10):
x = random.randint(0,20)
numL.append(x)
print(numL)
def F(x):
if numL[x] % 2 == 0:
return numL[x]
else:
return F(x+1)
print(F( ??? ))
First question asked on this forum, hopefully I did okay, appreciate any help!
Assuming you want to return a list of the even numbers then you have 4 cases to consider
This is the last number in the list and its even so return this number
This is the last number in the list and its odd dont retrun this number
There are more numbers to check and this number is even so return
this plus the function result
There are more numbers to check and this number is odd to return
only the function result and not this num
So we can code this as
import random
def get_even_nums(nums):
num = nums[0]
#This is our terminating case we definitivly return a value here
if len(nums) == 1:
return [num] if num % 2 == 0 else []
else:
#If we got here we will be recursivly calling the function
#If its even number return that number plus the result of the function
#it its not even then just return the reult of the function and not this num
if num % 2 == 0:
return [num] + get_even_nums(nums[1:])
else:
return get_even_nums(nums[1:])
numL = [random.randint(0, 20) for _ in range(10)]
print(numL)
print(get_even_nums(numL))
OUTPUT
[3, 6, 5, 10, 20, 18, 5, 0, 3, 9]
[6, 10, 20, 18, 0]
So I took your function and changed it up slightly (using a slightly different approach). There's no need to a global list, though you could do that as well, if you wanted. The problem that you have is the lack of a base case or rather an incorrect one.
If you run your original function with an argument 0, which basically is the first element of your generated array, the fucntion will run until it hits one even number. At that point it'll exit recursion, because the base case basically stops recursive calls once you hit an even number.
Now, to fix this, you have to approach the problem differently. I would put your generated array as the input argument to your function, then ask myself "What would be a good base case?" Probably one that stops your recursive calls once you reach the end of the input list.
if len(numL) == 0:
return ...
Also, we need a way to return the even numbers that we found during our search through the list. For that reason I'd introduce a new acc list, where we would append the even numbers that we found. Thus the function input arguments would be
def F(numL, acc):
...
Now, in the recursive call we should check wether the current element is even or not. If it is, great, we add it to the acc list and continue into the recursive call. If it's not, we don't add anything to the acc but just continue with recursion.
if numL[0] % 2 == 0:
acc.append(numL[0])
return F(numL[1:], acc)
Putting it all together, we get:
def F(numL, acc):
if len(numL) == 0:
return acc
else:
if numL[0] % 2 == 0:
acc.append(numL[0])
return F(numL[1:], acc)
where numL represents your generated list and acc represents the resulting list we'll return after we traverse the list.
This is your function (as I understand it, you wanted this):
import random
def F(i):
r = random.randint(0,20)
if r % 2 == 0:
print(r)
i += 1
if i != 10:
F(i)
F(0)

Finding multiples using recursion

Given 1 to 100 numbers, for multiples of 3 it should print "he" ,for multiples of 5 it should print "llo" ,for both multiples of 3 and 5 it should print "hello".
This is what I have:
for i in range (1,100):
if(i%3==0):
print("he")
elif(i%5==0):
print("llo")
elif(i%3==0 and i%5==0):
print("hello")
How would I do this recursively?
How about the code below?
def find_multiples(current, last_num=100):
# Base Case
if current > last_num:
return
result = ""
if current % 3 == 0:
result += "he"
if current % 5 == 0:
result += "llo"
if result:
print(f"{current}: {result}")
find_multiples(current+1, last_num)
find_multiples(1)
Base case is if current reaches last_num or the maximum number you'd like to check.
Here is a general outline for doing simple recursive things in python:
BASE_CASE = 1 #TODO
def f(current_case):
if current_case == BASE_CASE:
return #TODO: program logic here
new_case = current_case - 2 #TODO: program logic here ("decrement" the current_case somehow)
#TODO: even more program logic here
return f(new_case) + 1 #TODO: program logic here
Of course, this doesn't handle all possible recursive programs. However, it fits your case, and many others. You would call f(100), 100 would be current_value, you check to see if you've gotten to the bottom yet, and if so, return the appropriate value up the call stack. If not, you create a new case, which, in your case, is the "decrement" logic normally handled by the "loop" construct. You then do things for the current case, and then call the function again on the new case. This repeated function calling is what makes it "recursive". If you don't have an "if then" at the beginning of the function to handle the base case, and somewhere in the function recall the function on a "smaller" value, you're probably going to have a bad time with recursion.
This recursive function prints multiples of a number! hope it helps
def multi(n,x):
if x == 12:
print(n*x)
else :
print(n*x,end =",")
multi(n,x+1)
print(multi(4,1));

How to decrease running time of this particular solution?

I am trying to write a piece of code that will generate a permutation, or some series of characters that are all different in a recursive fashion.
def getSteps(length, res=[]):
if length == 1:
if res == []:
res.append("l")
res.append("r")
return res
else:
for i in range(0,len(res)):
res.append(res[i] + "l")
res.append(res[i] + "r")
print(res)
return res
else:
if res == []:
res.append("l")
res.append("r")
return getSteps(length-1,res)
else:
for i in range(0,len(res)):
res.append(res[i] + "l")
res.append(res[i] + "r")
print(res)
return getSteps(length-1,res)
def sanitize(length, res):
return [i for i in res if len(str(i)) == length]
print(sanitize(2,getSteps(2)))
So this would return
"LL", "LR", "RR, "RL" or some permutation of the series.
I can see right off the bat that this function probably runs quite slowly, seeing as I have to loop through an entire array. I tried to make the process as efficient as I could, but this is as far as I can get. I know that some unnecessary things happen during the run, but I don't know how to make it much better. So my question is this: what would I do to increase the efficiency and decrease the running time of this code?
edit = I want to be able to port this code to java or some other language in order to understand the concept of recursion rather than use external libraries and have my problem solved without understanding it.
Your design is broken. If you call getSteps again, res won't be an empty list, it will have garbage left over from the last call in it.
I think you want to generate permutations recursively, but I don't understand where you are going with the getSteps function
Here is a simple recursive function
def fn(x):
if x==1:
return 'LR'
return [j+i for i in fn(x-1) for j in "LR"]
Is there a way to combine the binary approach and a recursive approach?
Yes, and #gribbler came very close to that in the post to which that comment was attached. He just put the pieces together in "the other order".
How can you construct all the bitstrings of length n, in increasing order (when viewed as binary integers)? Well, if you already have all the bitstrings of length n-1, you can prefix them all with 0, and then prefix them all again with 1. It's that easy.
def f(n):
if n == 0:
return [""]
return [a + b for a in "RL" for b in f(n-1)]
print(f(3))
prints
['RRR', 'RRL', 'RLR', 'RLL', 'LRR', 'LRL', 'LLR', 'LLL']
Replace R with 0, and L with 1, and you have the 8 binary integers from 0 through 7 in increasing order.
You should look into itertools. There is a function there called permutations which does exactly what you want to achieve here.

Python return statement not running

I am creating a program to figure out the highest number of decimals in a list of numbers. Basically, a list with [123, 1233] would return 4 because 1233 has four numbers in it and it is the largest. Another example would be that [12, 4333, 5, 555555] would return 6 because 555555 has 6 numbers.
Here is my code.
def place(listy):
if len(listy) == 1:
decimal = len(str(listy[0]))
print(decimal)
else:
if len(str(listy[0])) >= len(str(listy[1])):
new_list = listy[0:1]
for i in listy[2:]:
new_list.append(i)
place(new_list)
else:
place(listy[1:])
Now, when I use print(decimal) it works, but if I change print(decimal) to return decimal, it doesn't return anything. Why is this? How do I fix this? I have come across these return statements which doing run a lot of times. Thanks in advance!
When you do a recursive call (i.e. when place calls place, and the called place returns a value, then the calling place must return it as well (i.e. the return value "bubbles up" to the initial caller).
So you need to replace every recursive call
place(...)
with
return place(...)
As others have said, there are easier solutions, such as using max(). If you want to keep a recursive approach, I would refactor your code as follows:
def place2(listy):
if len(listy) < 1:
return None
elif len(listy) == 1:
return len(str(listy[0]))
else:
v0, v1 = listy[0], listy[1]
if v1 > v0:
return place2(listy[1:])
else:
return place2([listy[0]]+listy[2:])
Although this is tail-recursive, Python does not really care so this approach will be inefficient. Using max(), or using a loop will be the better solution in Python.
It's not that the return doesn't do anything, it's that you don't propagate the return from your recursive call. You need a few more returns:
def place(listy):
if len(listy) == 1:
decimal = len(str(listy[0]))
return decimal
else:
if len(str(listy[0])) >= len(str(listy[1])):
new_list = listy[0:1]
for i in listy[2:]:
new_list.append(i)
return place(new_list) # <-- return added
else:
return place(listy[1:]) # <-- return added
You can see the print at any level, but to get it back to the caller it needs to be propagated.
The function does return the value, but it's not printing it out.
A simple way to solve this is, just call the function within a print statement.
That is:
print(place(listy))
If all you want is to find the maximum length of a list of integers, consider:
max([len(str(n)) for n in N])
For example
N = [1,22,333,4444]
max([len(str(n)) for n in N]) # Returns 4
N = [12, 4333, 5, 555555]
max([len(str(n)) for n in N]) # Returns 6
Note: This will only work for positive integers.
Or more simply:
len(str(max(N)))
Which will also only work for positive integers.
Use ''global variable'' (google it) to access and change a variable defined outside of your function.

Categories

Resources