Recursively running a for loop within a for loop (python) - python

I'm new to python and had the idea of trying to use it to solve the numbers game on the TV show countdown. (rules for those unfamiliar). I googled and it turns out this has been done before - But I didn't understand the code properly and then thought why not just give it a go myself. I have searched and there are other people looking for recursive solutions but I couldn't get them to work for my example (Apologies, I am very new to this after all).
What I'm trying to do is to take a list of numbers, then loop through applying operations to pairs of them and replacing that pair with the output. This would repeat recursively until either we find the number we are looking for, or the list of numbers is reduced to size 1.
My function "single_move_generator" is a generator producing tuples of the form ((a,b,operation), answer, numbers left to use). I want to feed this final part of the tuple back into the function as the new list, but also keep track of the first part as it is the 'history' of how we achieved our answer. At the moment I have the following:
target = 155
numbers_to_use = [6, 25, 3, 2]
for a in single_move_generator(numbers):
if a[1] == target:
print(a[0])
for b in single_move_generator(a[2]):
if b[1] == target:
print(a[0],b[0])
quit()
for c in single_move_generator(b[2]):
if c[1] == target:
print(a[0],b[0],c[0])
quit()
produces:
(25, 6, 'add') (3, 2, 'add') (31, 5, 'multiply')
But I'd like to able to give it a larger list of numbers and have it just continue until the list reaches size one. I suspect I need a while loop - but this attempt doesn't work. It doesn't find the target or keep track of the history of moves.
numbers_available = numbers
while len(numbers_available) >1 and target not in numbers_available:
for a in single_move_generator(numbers_available):
if a[1] == target:
print("Target Found", a)
break
numbers_available = a[2]
numbers_available = a[2]
I feel like there must be a pythonic way of doing this which is far neater than I've done - any hints would be greatly appreciated. Thanks!

Based on your idea of using tuples (i, j, operation), I wrote the following. This is a recursive solution, as the main function calls itself back.
from itertools import combinations, product
def check_operation(i, j, operation):
"""
Check whether 'i operation j' is permitted.
"""
if operation == '/' and j == 0:
return False
elif operation == '/' and i%j != 0:
return False
# if not playing with negative values
#elif operation == '-' and i-j < 0:
# return False
else:
return True
def countdown(target, numbers, trackback):
if target in numbers:
print trackback
for possibility in product(combinations(numbers,2), ['+', '*', '/', '-']):
new_numbers = [k for k in numbers] # copy list, use list.copy in python 3
i, j = possibility[0][0], possibility[0][1]
operation = possibility[1]
new_numbers.remove(i)
new_numbers.remove(j)
if not check_operation(i, j, operation):
continue
new_numbers.append(eval('%i%s%i' % (i, operation, j)))
countdown(target, new_numbers, trackback+[possibility])
countdown(155, [6, 25, 3, 2], [])
It only works if a solution exists, since it won't intend to get as close to the solution as possible. However it will return all the solutions, not only one.

Based on what you posted, this should work for you:
def solve(numbers, target):
for op, res, nums in single_move_generator(numbers):
if res == target:
return [op]
ops = solve(nums, target)
if ops:
return [op] + ops
print(*solve(numbers_to_use, target))
It should be equivalent to the nested for loop you posted.
Bottom of the recursion is hit when res == target. Python functions return None by default, so if a recursive call to solve returned a truthy value then it must have hit the target. ops will contain the last operation if it was the bottom of the recursion. It is then appended to the operation that launched the recursive call and returned to an upper level. So the function will return all operations at the top level.

Related

can't quite figure out what's not working (checkio excersise "Even the Last")

You are given an array of integers. You should find the sum of the integers with even indexes (0th, 2nd, 4th...). Then multiply this summed number and the final element of the array together. Don't forget that the first element has an index of 0.
For an empty array, the result will always be 0 (zero).
Input: A list of integers.
Output: The number as an integer.
Precondition: 0 ≤ len(array) ≤ 20
all(isinstance(x, int) for x in array)
all(-100 < x < 100 for x in array
result = 0
if array:
for element in array:
i = array.index(element)
if i%2 == 0:
result += element
else:
pass
else:
return 0
return result
Last_digit = array[-1]
final_result = result*Last_digit
return final_result
print(final_result)```
I've figured out the problem, that you've shared the array you're having problem with. Since you have this array :
[-37,-36,-19,-99,29,20,3,-7,-64,84,36,62,26,-76,55,-24,84,49,-65,41]
If you notice here, 84 appears twice, first at index 9 and then 16. The method you're using to get index of elements, .index returns the index of the first instance the element is found in the list.Therefore for the value of 84, the index is taken as 9 and not 16 which is an odd value, this does not add 84 to your sum. You should rather use enumerate for your code:
for idx, element in enumerate(array):
if idx %2 == 0:
result += element
First, I recommend reading the stackexchange guides on posting a well-formed question. You need to state what your goal is, what you've tried, what errors get thrown, and what the output should look like -- along with code examples and a minimal reproducible example as needed.
However, I'll help you out anyway.
You have a dangling return at line 11:
else:
return 0
return result
This makes no sense, as you've already returned 0. This is also apparently a snippet from a function, no? Post the whole function. But based on the instructions, you could try this:
import random
array = random.sample(range(-100, 100), 20)
def etl_func(arr):
arrsum = 0
for i, val in enumerate(arr):
if i%2 == 0: arrsum += val
return (arrsum * arr[-1])
answer = etl_func(array)
print(answer)
Note that importing random and using array = random.sample(range(-100, 100), 20) are not necessary if you're already GIVEN an array to work with. They're included here just as an example.
Also note that it's unnecessary to use an else: pass. If the condition evaluates to true (i.e. i%2 == 0), the if block will be executed. If i%2 != 0, the loop will short circuit automatically and move to the next iteration. Adding else: pass is like telling someone sitting in your chair to sit in your chair. You're telling the program to do what it's already going to do anyway. There's nothing necessarily wrong with including the else: pass, if it really want to... but it's just adding lines of meaningless code, which nobody wants to deal with.
EDIT: I don't know whether you were supposed to write a function or just some code (back to the "ask a well-formed question" issue...), so I went with a function. It should be trivial to turn the function into just plain code -- but you want to get into the habit of writing good functions for reusability and modularity. Makes everything run more smoothly and elegantly, and makes troubleshooting much easier.
This function also works for the array mentioned in the comments to your original post.
In addition, if you need a direct replacement for your code (rather than a function... I'm not familiar with checkio or how your answers are supposed to be formatted), and you already have the array of integers stored in the variable array, try this:
arrsum = 0
for i, val in enumerate(array):
if i%2 == 0: arrsum += val
print(arrsum * array[-1])
Since your question didn't say anything about using or defining functions, return statements shouldn't appear anywhere. There's nothing to return unless you're writing a function.

Stuck at Python Solution for AlmostIncreasingSequence (CodeSignal)

I was working on this particular task given by CodeSignal which had the purpose of creating a program that would go through a given list with numbers and check if the numbers were increasing and/or "almost increasing". What this means is that, for example, if the list was (1, 3, 2, 1) then the program should return false due to the fact that, while it does appear to increase, it also takes a dip back to 1, returning false. Getting true on the output would require the list incrementing up normally, or almost incrementing up like (1, 3, 2, 4).
I created a code for this through research and Youtube videos, and I actually understood how the code worked and tried it out on Python, however only getting 12/19 correct. I did think this code would go through and ace all the 19 tests but it turns out it had an issue within it and would like to know how I could fix this. The code:
def almostIncreasingSequence(sequence):
count = 0
for i in range(0, len(sequence)-1):
if sequence[i] <= sequence[i-1]:
count +=1
if sequence[i] <= sequence[i-2] and sequence[i+1] <= sequence[i-1]:
return False
return count <= 1
The outputs:
I have tried to give the best solution possible with me but unfortunately ended up with time limit exceeded for last 4 test cases.
Explanation
each time I tried with excepting one element from an array and checked if it is in sorted order or not and thus i continued it until last if i did not found any such possible arrays i returned false
def solution(sequence):
if sorted(set(sequence)) == sequence :
return True
for i in range(len(sequence)):
temp = sequence[:i]+sequence[i+1:]
if sorted(set(temp)) == temp :
return True
return False
def isIncreasing(sequence):
return sorted(set(sequence)) == sequence
def isAlmostIncreasing(sequence):
#(optional)
#if isIncreasing(sequence) :
# return True
for i in range(len(sequence) - 1):
if sequence[i] >= sequence[i+1]:
temp, temp2 = sequence[:], sequence[:]
del temp[i]
del temp2[i+1]
return isIncreasing(temp) or isIncreasing(temp2)
return True
First, if sequence is already IncreasingSequence then no need to check individual data.(optional)
but if it's not:
find first wrong pair at index i & i+1. (if no wrong pair found, then sequence has no issue.)
if we remove one of them, check will we get proper result in remaining sequence?

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)

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.

How to rewrite this function as a recursive function?

def digits(n):
res = []
while n > 0:
res.append(n % 10)
n /= 10
return res
I want to rewrite this function so it uses recursion. I'm currently lost as to what to do. Can anyone give me some direction?
To create a recursive function you need to determine two things:
1) The base case - the condition on which you want to stop recursion
2) The general case - what to do on every input but the base case
You have already found both of these things, the base case is the while loop condition and the general case is the inside of the while loop. Try to use this information to move forward.
Here's a possible solution:
def digits(n):
if n < 10:
return [n]
return digits(n/10) + [n%10]
digits(123)
> [1, 2, 3]
The above solution fixes a bug in your code, you were returning the digits in reverse order. Also notice that n must be an integer greater than or equal to zero for producing correct results.
Here's how it works:
If the number is less than 10, then return a list with the number, as there are no more digits to be processed
If the number is greater than 9, get the last digit in the current number and add it to the end of the list that results of recursively calling digits on a smaller number - namely, the number without the last digit that we just processed.
The call to digits(123) will look like this at each step of the recursion:
digits(123) = digits(123/10) + [3]
digits(12) = digits(12/10) + [2]
digits(1) = [1]
Now we go up the call stack:
[1]
[1] + [2]
[1, 2] + [3]
[1, 2, 3]
EDIT :
Accepting #thg435's challenge, here's a tail-recursive solution:
def digits(n):
def loop(i, acc):
if i < 10:
return [i] + acc
return loop(i/10, [i%10] + acc)
return loop(n, [])
When you use recursion, a good basis is to have two cases to check, a base case and a recursive case. The base case is the conditions and result under which the program returns, in your case, the base case would be when n > 0 (If you think of it like a while loop, it's the condition for the while loop exiting). The recursive case (there can be multiple of these) occur when the loop isn't done, if you compare to a while loop, this is basically the body of the loop. At the end of the recursive case, you need to call the function again with your changes to the input, in this case n/10.
So, your function definition would be something like:
def digits(n):
For the base case, you want to check if n is 0, and, if it is, return the empty list:
if n <= 0:
return []
Now, in the recursive case, you want to append n%10 to the list and call your function again, only you want to call it with a different n, changed as you had it in your while loop:
else:
return [n%10]+digits(n/10)
So, if you trace this through, for every recursive case, you get a list containing n%10, then it adds the result of the new call, which will be either (n/10)%10 or the empty list. For example, running this function with n=100 would break down like this:
newlist = digits(100)
newlist = [100%10]+digits(100/10)
newlist = [100%10]+([10%10] + digits(10/10))
newlist = [100%10]+([10%10] + ([1%10] + digits(10/10)))
newlist = [100%10]+([10%10] + ([1%10] + ([])))
newlist = [0,0,1]
Nested parens are used to show how the function digits gets rewritten inline.
def digits(n):
res = []
res.append(n%10)
n /= 10
if n != 0:
return res + digits(n)
else:
return res

Categories

Resources