Returning value from recursion - python

I was following a tutorial to All unique combinations whose sum equals to K from here. I need to tinker with the program to return the first unique combination found.
So I modified the code to return a final array which I pass in the combination_rec as well. Debugging the code in I see that the state of final is not maintained across the recursive stack, I get [] as the returned value.
import copy
def combination(arr, sum):
arr.sort(reverse=False)
local = []
res = combination_rec(0, 0, sum, local, arr, [])
if res:
return res
return []
def combination_rec(length, total, sum, local, arr, final):
if total == sum:
final = copy.copy(local)
return final
for i in range(length, len(arr), 1):
if total + arr[i] > sum:
continue
if (i > length and arr[i] == arr[i - 1]):
continue
local.append(arr[i])
final = combination_rec(i + 1, total + arr[i], sum, local, arr, final)
local.remove(local[len(local) - 1])
return final
if __name__ == "__main__":
combination([6, 5, 4, 3], 8)
Expected Output
--------
[3,5]
Actual Output
--------
[]
Summary of the program
Sorts the array - [3, 4, 4, 5, 6]
Loops over its elements to check each possible combination of elements which can sum up to K (8)
Begins with 3. Checks if subarray [3, 4] totals 8. It does not.
So it modifies the sub-array to include [3, 4, 5]. As this total (12) > 8
It goes back to line local.remove(local[len(local) - 1]) to remove 5 from sub-array and proceed to consider subsequent elements in the sorted array
Checks for [3,4,6].
Removes 6, followed by removal of 4.
Next check is for [3, 5]. This matches the total and this is when I wish to return final as [3, 5]
What is it I am doing wrong?
PS: I know that step 4 - the subsequent for loop iterations - is redundant. It does not make sense to check for [3,4,6] when [3,4,5] yielded a sum greater than it. But that's not my concern right. I am trying to understand returning values from recursion

Related

How many steps to the nearest zero value

Looking for some help with solving a seemingly easy algorithm.
Brief overview:
We have an unsorted list of whole numbers. The goal is to find out how far each element of this list is from the nearest '0' value.
So if we have a list similar to this: [0, 1, 2, 0, 4, 5, 6, 7, 0, 5, 6, 9]
The expected result will be: [0, 1, 1, 0, 1, 2, 2, 1, 0, 1, 2, 3]
I've tried to simplify the problem in order to come up with some naive algorithm, but I can't figure out how to keep track of previous and next zero values.
My initial thoughts were to figure out all indexes for zeros in the list and fill the gaps between those zeros with values, but this obviously didn't quite work out for me.
The poorly implemented code (so far I'm just counting down the steps to the next zero):
def get_empty_lot_index(arr: list) -> list:
''' Gets all indices of empty lots '''
lots = []
for i in range(len(arr)):
if arr[i] == 0:
lots.append(i)
return lots
def space_to_empty_lots(arr: list) -> list:
empty_lots = get_empty_lot_index(arr)
new_arr = []
start = 0
for i in empty_lots:
steps = i - start
while steps >= 0:
new_arr.append(steps)
steps -= 1
start = i + 1
return new_arr
One possible algorithm is to make two sweeps through the input list: once forward, once backward. Each time retain the index of the last encountered 0 and store the difference. In the second sweep take the minimum of what was stored in the first sweep and the new result:
def space_to_empty_lots(arr: list) -> list:
result = []
# first sweep
lastZero = -len(arr)
for i, value in enumerate(arr):
if value == 0:
lastZero = i
result.append(i - lastZero)
# second sweep
lastZero = len(arr)*2
for i, value in reversed(list(enumerate(arr))):
if value == 0:
lastZero = i
result[i] = min(result[i], lastZero - i)
return result
NB: this function assumes that there is at least one 0 in the input list. It is not clear what the function should do when there is no 0. In that case this implementation will return a list with values greater than the length of the input list.

Why control is going inside if condition while the condition is false

Function to find minimum number of eliminations such that sum of all adjacent elements is even:
def min_elimination(n, arr):
countOdd = 0
# Stores the new value
for i in range(n):
# Count odd numbers
***if (arr[i] % 2):
countOdd += 1***
# Return the minimum of even and
# odd count
return min(countOdd, n - countOdd)
# Driver code
if __name__ == '__main__':
arr = [1, 2, 3, 7, 9]
n = len(arr)
print(min_elimination(n, arr))
Please help me with the if condition. When the code does if(number%2) then control is going inside the if since the first element of list is an odd number. Is there any difference between if(number%2) and if(number%2==0). Because when I tried if(number%2==0) control didn't go inside the if as the number was odd (check first element of the list).
This is a simple version of the above.
def min_elimination(arr):
lst1 = [n for n in arr if n%2] # List of all odd numbers
lst2 = [n for n in arr if not n%2] # List of all even numbers
lst = max(lst1, lst2, key=lambda x: len(x))
return lst
print(min_elimination([1, 2, 3, 7, 9]))
I believe your code works fine as-is. It correctly returns the minimum number of eliminations necessary to achieve the result, not the result itself.
is there any difference between if(number%2) and if(number%2==0)
Yes, if number % 2 is the same as saying if number % 2 == 1, which is the opposite of saying if number % 2 == 0. So switching one for the other will break your program's logic.
I might simplify your code as follows:
def min_elimination(array):
odd = 0
# Stores the new value
for number in array:
# Count odd numbers
odd += number % 2
return min(odd, len(array) - odd)
Where min_elimination([1, 2, 3, 7, 9]) returns 1, there is one (1) elimination necessary to make the sum of all adjacent elements even.
The only way two integers can add up to be even is that both integers should be odd, or both integers should be even. So your question basically wants to find whether there are more odd number in the array, or even numbers:
def min_elimination(arr):
return len(min([n for n in arr if n%2],[n for n in arr if not n%2],key=len))
print(min_elimination([1, 2, 3, 7, 9]))
Output:
1
You can use numpy to compare the number of odd numbers in the array to the number of even numbers in the array:
import numpy as np
def min_elimination(arr):
return len(min(arr[arr%2],arr[any(arr%2)],key=len))
print(min_elimination(np.array([1, 2, 3, 7, 9])))
Output:
1

Trying to optimize this code: iterating over a list to replace its values

I am trying to do a challenge in Python, the challenge consists of :
Given an array X of positive integers, its elements are to be transformed by running the following operation on them as many times as required:
if X[i] > X[j] then X[i] = X[i] - X[j]
When no more transformations are possible, return its sum ("smallest possible sum").
Basically you pick two non-equal numbers from the array, and replace the largest of them with their subtraction. You repeat this till all numbers in array are same.
I tried a basic approach by using min and max but there is another constraint which is time. I always get timeout because my code is not optimized and takes too much time to execute. Can you please suggest some solutions to make it run faster.
def solution(array):
while len(set(array)) != 1:
array[array.index(max(array))] = max(array) - min(array)
return sum(array)
Thank you so much !
EDIT
I will avoid to spoil the challenge... because I didn't find the solution in Python. But here's the general design of an algorithm that works in Kotlin (in 538 ms). In Python I'm stuck at the middle of the performance tests.
Some thoughts:
First, the idea to remove the minimum from the other elements is good: the modulo (we remove the minimum as long as it is possible) will be small.
Second, if this minimum is 1, the array will be soon full of 1s and the result is N (the len of the array).
Third, if all elements are equal, the result is N times the value of one element.
The algorithm
The idea is to keep two indices: i is the current index that cycles on 0..N and k is the index of the current minimum.
At the beginning, k = i = 0 and the minimum is m = arr[0]. We advance i until one of the following happen:
i == k => we made a full cycle without updating k, return N*m;
arr[i] == 1 => return N;
arr[i] < m => update k and m;
arr[i] > m => compute the new value of arr[i] (that is arr[i] % m or m if arr[i] is a multiple of m). If thats not m, thats arr[i] % m < m: update k and m;
arr[i] == m => pass.
Bascially, we use a rolling minimum and compute the modulos on the fly until all element are the same. That spares the computation of a min of the array periodically.
PREVIOUS ANSWER
As #BallpointBen wrote, you'll get the n times the GCD of all numbers. But that's cheating ;)! If you want to find a solution by hand, you can optimize your code.
While you don't find N identical numbers, you use the set, max (twice!), min and index functions on array. Those functions are pretty expensive. The number of iterations depend on the array.
Imagine the array is sorted in reverse order: [22, 14, 6, 2]. You can replace 22 by 22-14, 14 by 14-6, ... and get: [8, 12, 4, 2]. Sort again: [12, 8, 4, 2], replace again: [4, 4, 4, 2]. Sort again, replace again (if different): [4, 4, 2, 2], [4, 2, 2, 2], [2, 2, 2, 2]. Actually, in the first pass 14 could be replaced by 14-2*6 = 2 (as in the classic GCD computation), giving the following sequence:
[22, 14, 6, 2]
[8, 2, 2, 2]
[2, 2, 2, 2]
The convergence is fast.
def solution2(arr):
N = len(arr)
end = False
while not end:
arr = sorted(arr, reverse=True)
end = True
for i in range(1, N):
while arr[i-1] > arr[i]:
arr[i-1] -= arr[i]
end = False
return sum(arr)
A benchmark:
import random
import timeit
arr = [4*random.randint(1, 100000) for _ in range(100)] # GCD will be 4 or a multiple of 4
assert solution(list(arr)) == solution2(list(arr))
print(timeit.timeit(lambda: solution(list(arr)), number=100))
print(timeit.timeit(lambda: solution2(list(arr)), number=100))
Output:
2.5396839629975148
0.029025810996245127
def solution(a):
N = len(a)
end = False
while not end:
a = sorted(a, reverse=True)
small = min(a)
end = True
for i in range(1, N):
if a[i-1] > small:
a[i-1] = a[i-1]%small if a[i-1]%small !=0 else small
end = False
return sum(a)
made it faster with a slight change
This solution worked for me. I iterated on the list only once. initially I find the minimum and iterating over the list I replace the element with the rest of the division. If I find a rest equal to 1 the result will be trivially 1 multiplied by the length of the list otherwise if it is less than the minimum, i will replace the variable m with the minimum found and continue. Once the iteration is finished, the result will be the minimum for the length of the list.
Here the code:
def solution(a):
L = len(a)
if L == 1:
return a[0]
m=min(a)
for i in range(L):
if a[i] != m:
if a[i] % m != 0:
a[i] = a[i]%m
if a[i]<m:
m=a[i]
elif a[i] % m == 0:
a[i] -= m * (a[i] // m - 1)
if a[i]==1:
return 1*L
return m*L

Compare if every element of a list is larger then every other elements

I want to compare if element one of a list is larger then every other element(same for every other element).
If one element is larger than an other it gets a 1. The sum of 1s (depending on the number of comparison "won") should be store in a way that let me know how many comparison are wow for each specif element of the list.
To clarify every element to a list would be an individual with an ID
Python
#Here I create 10 random values which I call individual with the random
#funcion plus mean and standard deviation
a, b = 3, 10
mu, sigma = 5.6, 2
dist = stats.truncnorm((a - mu) / sigma, (b - mu) / sigma, loc=mu, scale=sigma)
individuals = dist.rvs(10)
#Initialize the list where I want to store the 1s
outcome = num.zeros(n)
#Trying to loop through all the elements
for k in range(0, n):
for j in range(0, n):
if individuals[k] == individuals[j]:
continue
elif individuals[k] < individuals[j]:
continue
elif individuals[k] > individuals[j]:
outcome[i] += 1
return outcome[i]
I end up having an outcome with one single value.
Probably it summed up every 1s in the first element
Here is a more efficient way, by sorting the list first, which makes the process O(n*log(n)) instead of O(n**2).
We sort the list, keeping the original index of each value (this is O(n*log(n)).
Then, we go once over the list to set the output counts, which are the indices of the values in the sorted list, except for the duplicates - in this case, we just keep track of the number of identical values to adjust the result.
def larger_than(values):
ordered_values = sorted((value, index) for index, value in enumerate(values))
out = [None] * len(values)
# take care of equal values
equals = 0
prev = None
for rank, (value, index) in enumerate(ordered_values):
if value == prev:
equals += 1
else:
equals = 0
prev = value
out[index] = rank - equals
return out
Some test:
values = [1, 4, 3, 3, 10, 1, 5, 2, 7, 6]
print(larger_than(values))
# [0, 5, 3, 3, 9, 0, 6, 2, 8, 7]

N random, contiguous and non-overlapping subsequences each of length

I'm trying to get n random and non-overlapping slices of a sequence where each subsequence is of length l, preferably in the order they appear.
This is the code I have so far and it's gotten more and more messy with each attempt to make it work, needless to say it doesn't work.
def rand_parts(seq, n, l):
"""
return n random non-overlapping partitions each of length l.
If n * l > len(seq) raise error.
"""
if n * l > len(seq):
raise Exception('length of seq too short for given n, l arguments')
if not isinstance(seq, list):
seq = list(seq)
gaps = [0] * (n + 1)
for g in xrange(len(seq) - (n * l)):
gaps[random.randint(0, len(gaps) - 1)] += 1
result = []
for i, g in enumerate(gaps):
x = g + (i * l)
result.append(seq[x:x+l])
if i < len(gaps) - 1:
gaps[i] += x
return result
For example if we say rand_parts([1, 2, 3, 4, 5, 6], 2, 2) there are 6 possible results that it could return from the following diagram:
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
So [[3, 4], [5, 6]] would be acceptable but [[3, 4], [4, 5]] wouldn't because it's overlapping and [[2, 4], [5, 6]] also wouldn't because [2, 4] isn't contiguous.
I encountered this problem while doing a little code golfing so for interests sake it would also be nice to see both a simple solution and/or an efficient one, not so much interested in my existing code.
def rand_parts(seq, n, l):
indices = xrange(len(seq) - (l - 1) * n)
result = []
offset = 0
for i in sorted(random.sample(indices, n)):
i += offset
result.append(seq[i:i+l])
offset += l - 1
return result
To understand this, first consider the case l == 1. Then it's basically just returning a random.sample() of the input data in sorted order; in this case the offset variable is always 0.
The case where l > 1 is an extension of the previous case. We use random.sample() to pick up positions, but maintain an offset to shift successive results: in this way, we make sure that they are non-overlapping ranges --- i.e. they start at a distance of at least l of each other, rather than 1.
Many solutions can be hacked for this problem, but one has to be careful if the sequences are to be strictly random. For example, it's wrong to begin by picking a random number between 0 and len(seq)-n*l and say that the first sequence will start there, then work recursively.
The problem is equivalent to selecting randomly n+1 integer numbers such that their sum is equal to len(seq)-l*n. (These numbers will be the "gaps" between your sequences.) To solve it, you can see this question.
This worked for me in Python 3.3.2. It should be backwards compatible with Python 2.7.
from random import randint as r
def greater_than(n, lis, l):
for element in lis:
if n < element + l:
return False
return True
def rand_parts(seq, n, l):
"""
return n random non-overlapping partitions each of length l.
If n * l > len(seq) raise error.
"""
if n * l > len(seq):
raise(Exception('length of seq too short for given n, l arguments'))
if not isinstance(seq, list):
seq = list(seq)
# Setup
left_to_do = n
tried = []
result = []
# The main loop
while left_to_do > 0:
while True:
index = r(0, len(seq) - 1)
if greater_than(index, tried, l) and index <= len(seq) - left_to_do * l:
tried.append(index)
break
left_to_do -= 1
result.append(seq[index:index+l])
# Done
return result
a = [1, 2, 3, 4, 5, 6]
print(rand_parts(a, 3, 2))
The above code will always print [[1, 2], [3, 4], [5, 6]]
If you do it recursively it's much simpler. Take the first part from (so the rest will fit):
[0:total_len - (numer_of_parts - 1) * (len_of_parts)]
and then recurse with what left to do:
rand_parts(seq - begining _to_end_of_part_you_grabbed, n - 1, l)
First of all, I think you need to clarify what you mean by the term random.
How can you generate a truly random list of sub-sequences when you are placing specific restrictions on the sub-sequences themselves?
As far as I know, the best "randomness" anyone can achieve in this context is generating all lists of sub-sequences that satisfy your criteria, and selecting from the pool however many you need in a random fashion.
Now based on my experience from an algorithms class that I've taken a few years ago, your problem seems to be a typical example which could be solved using a greedy algorithm making these big (but likely?) assumptions about what you were actually asking in the first place:
What you actually meant by random is not that a list of sub-sequence should be generated randomly (which is kind of contradictory as I said before), but that any of the solutions that could be produced is just as valid as the rest (e.g. any of the 6 solutions is valid from input [1,2,3,4,5,6] and you don't care which one)
Restating the above, you just want any one of the possible solutions that could be generated, and you want an algorithm that can output one of these valid answers.
Assuming the above here is a greedy algorithm which generates one of the possible lists of sub-sequences in linear time (excluding sorting, which is O(n*log(n))):
def subseq(seq, count, length):
s = sorted(list(set(seq)))
result = []
subseq = []
for n in s:
if len(subseq) == length:
result.append(subseq)
if len(result) == count:
return result
subseq = [n]
elif len(subseq) == 0:
subseq.append(n)
elif subseq[-1] + 1 == n:
subseq.append(n)
elif subseq[-1] + 1 < n:
subseq = [n]
print("Impossible!")
The gist of the algorithm is as follows:
One of your requirements is that there cannot be any overlaps, and this ultimately implies you need to deal with unique numbers and unique numbers only. So I use the set() operation to get rid all the duplicates. Then I sort it.
Rest is pretty straight forward imo. I just iterate over the sorted list and form sub-sequences greedily.
If the algorithm can't form enough number of sub-sequences then print "Impossible!"
Hope this was what you were looking for.
EDIT: For some reason I wrongly assumed that there couldn't be repeating values in a sub-sequence, this one allows it.
def subseq2(seq, count, length):
s = sorted(seq)
result = []
subseq = []
for n in s:
if len(subseq) == length:
result.append(subseq)
if len(result) == count:
return result
subseq = [n]
elif len(subseq) == 0:
subseq.append(n)
elif subseq[-1] + 1 == n or subseq[-1] == n:
subseq.append(n)
elif subseq[-1] + 1 < n:
subseq = [n]
print("Impossible!")

Categories