Retaining information through recursion under strict conditions (Python) - python

My partner and I are working on a problem in which we need to reduce an array and run operations on the pieces (in this case, of 2), we are reducing with recursion and sending the left-right hand sides of the array back into function.
We have all this working fine, and for example, using the array [2, 3, 4, 5, 6, 7, 8, 9] we get all the pieces we need.
The problem is, we need each of these pieces to then run the math operation on the next piece we get.
So, for example we recur down to [2, 3] and transform that into [5, -1]. Then we recur down to [4, 5] and change that into [9, -1] and combine them into [14, -2, -4, 0]. This is the left hand side of our array. And this works great. And then it does the right hand side, and it gets the answer we want. This works great.
The problem, is we now need both parts together (can't use global variables). And we have been stuck here for several hours. We can only pass the simplified array through the recursion, and if we initialize an array to hold both parts it will get reset when the right-hand side starts.
Thanks
EDIT: Code: The H is starting matrix that is given, but it doesn't matter, it as no relevance its just there so the unit test goes through (We could use it, but we don't really know how)
The imput for x is [2,3,4,5,6,7,8,9]
def hadmatmult(H, x):
d = 0
n = len(x)
first = 0
last = len(x)
a = [0] * math.floor(n/2)
b = [0] * math.floor(n/2)
if n == 2:
temp1 = x[0]
x[0] = temp1 + x[1]
x[1] = temp1 - x[1]
else:
mid = math.floor((first+last)/2)
for i in range(first, mid):
a[i] = x[i]
hadmatmult(H, a)
for j in range(mid, last):
b[d] = x[j]
d = d + 1
hadmatmult(H, b)
if(len(a) == 2:
adds = [0] * len(a)
subs = [0] * len(a)
for t in range(0, len(a)):
adds[t] = a[t] + b[t]
subs[t] = a[t] - b[t]
#alladds = alladds + adds
#allsubs = allsubs + subs
print(adds)
print(subs)
Output: This outputs the parts, [14, -2, -4, 0] and [30, -2, -4, 0]

If you must recurse, do so in a simpler way. Strip off the elements you need and recurse through the rest.
x = [2,3,4,5,6,7,8,9]
def recurse_and_stuff(x):
if len(x) == 0:
# base case
return []
a, b, = x[:2], x[2:4]
m,n,o,p = [a[0]+a[1], a[0]-a[1], b[0]+b[1], b[0]-b[1]]
result = [[m+o, n+p, m-o, n-p]] + recurse_and_stuff(x[4:])
return result
>>> x = [2,3,4,5,6,7,8,9]
>>> recurse_and_stuff(x)
[[14, -2, -4, 0], [30, -2, -4, 0]]
>>> x = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
>>> recurse_and_stuff(x)
[[14, -2, -4, 0], [30, -2, -4, 0], [46, -2, -4, 0], [62, -2, -4, 0]]
# works for longer inputs as long as len(x) % 4 == 0
To recurse all the way down:
import itertools
def do_the_needful(a,b):
try:
zipped = zip(a,b)
except TypeError:
# a and b are ints, not lists of ints
zipped = [(a,b)]
return itertools.chain.from_iterable(zip(*[[aa+bb, aa-bb] for aa,bb in zipped]))
def recurse_and_stuff(x):
if len(x) == 1:
return list(x[0])
# if you only have to iterate through this, there's no
# need to call `list(...)` here.
return recurse_and_stuff([do_the_needful(x[i], x[i+1]) for i in range(0, len(x), 2)])

Related

Given a list of integers, split it into 2 list that return True if it is equal, else, return False (If it is impossible)

Same as before, I am given a list of string, and I have to split that ONE list into two list to check if it is possible to have equal sum. If possible, return array 1 and array 2.
The following code works for ONE set of combination
Eg. myArr = [1, 2, 3, 4]
Each full iteration will do the following (targetSum = sum(myArr) / 2) #5
#1: PartialArr1 = [1] , PartialArr2 = [2,3,4] , Check if Arr1 == targetSum
#1: PartialArr1 = [1, 2] , PartialArr2 = [3,4] , Check if Arr1 == targetSum
#1: PartialArr1 = [1, 2, 3] , PartialArr2 = [4] , Check if Arr1 == targetSum
#1: PartialArr1 = [1, 2, 3, 4] , PartialArr2 = [] , Check if Arr1 == targetSum
While #1 does not returns any True value, permutate the number ONCE
#2: myArr = [2, 3, 4, 1]
#2: PartialArr1 = [2] , PartialArr2 = [3,4,1] , Check if Arr1 == targetSum
#2: PartialArr1 = [2, 3] , PartialArr2 = [4,1] , Check if Arr1 == targetSum #Return PartialArr1, PartialArr2
def group(s):
sumOfArr = sum(s)
isPossible = sumOfArr%2
if(isPossible == 0):
#No remainder, possible to get a combination of two equal arrays
targetSum = int(sumOfArr/2)
partialArr1 = []
partialArr2 = []
i = 0
while(targetSum != sum(partialArr1)):
partialArr1.append(s[i])
partialArr2 = s[i+1:]
i+=1
if(i == len(s)-1) or (sum(partialArr1) > targetSum):
partialArr1 = []
partialArr2 = []
i = 0
s = s[1:] + s[:1]
return partialArr1,partialArr2
else:
#Not possible, return None, None
return None, None
While my solution works for most permutation group, it doesn't work on the following:
#The following works with my code ->
#s = [135, 129, 141, 121, 105, 109, 105, 147]
#s = [-14, 3, 4, 13, -1, -5, 0, 5, -10, 8, -4, 10, -12, 11, 9, 12, -6, -11, -9, -8]
#s= [-1, 1, 4, 2, 8, 0]
#s = [10, 2,2,10, 2, 2, 2,10]
#SOLUTION SHOULD RETURN THE FOLLOWING
#s1 = [6, 10, 6, 10], sum(s1) = 32
#s2 = [7, -3, 1, -4, 2, 2, 7, -3, 2, 4, 0, 7, 6, -2, -4, 10], sum(s1) = 32
s = [7, -3, 6, 1, 10, -4, 2, 2, 6, 7, -3, 2, 4, 0, 7, 6, -2, 10, -4, 10]
group(s) #This does not work because it does not permutate all possible ways of len(s)^2 ways. Thus I am stucked at this point of time
Any help will be appreciated!! Thanks!!
From what I understood, you need to see if your array can be divided into 2 equal arrays and return True if it does else you return False, you don't need the arrays it gets divided to, just the fact that it can be divided.
Well if it your array can be divided into 2 equal arrays, some of its elements should sum to the half of the total sum of your array.
For example :
[5,3,5,2,10,1]
This array has the sum of 26. if some of its elements can be equal to 13 then it can be divided into 2 arrays of equal arrays, because the rest of the elements would sum to 13 too. example : [10,3] and [5,5,2,1], so you return True when you find that a combination of elements of your array equals half the sum of the total array. Also automatically an array with an odd sum is not dividable to 2 equal arrays. nb : searching for all combinations has a high complexity and will be slow with big lists.
This is what exactly i am doing, using itertools to get the combinations that sum to the half the total sum of the array, if there are combinations then it can be divided to 2 equal arrays, test it :
import itertools
numbers = [135, 129, 141, 121, 105, 109, 105, 147]
if((sum(numbers)%2)!=0):
print(False)
else:
result = [seq for i in range(len(numbers), 0, -1) for seq in itertools.combinations(numbers, i) if sum(seq) == (sum(numbers)//2)]
if len(result)>1:
print(True)
else:
print(False)
Here is the function that you need, it returns True when the array can be divided into 2 equal arrays and False if it cannot, without use of any libraries :
def search(numbers):
target = sum(numbers)/2
def subset_sum(numbers, target, partial=[]):
s = sum(partial)
if s == target:
print(5/0)
if s >= target:
return
for i in range(len(numbers)):
n = numbers[i]
remaining = numbers[i + 1:]
subset_sum(remaining, target, partial + [n])
try:
subset_sum(numbers, target)
return False
except:
return True
numbers = [135, 129, 141, 121, 105, 109, 105, 147]
print(search(numbers))

How to if given a list of integers and a number called x, return recursively the sum of every x'th number in the list

So, How to if given a list of integers and a number called x, return recursively the sum of every x'th number in the list.
In this task "indexing" starts from 1, so if x = 2 and nums = [2, 3, 4, -9], the output should be -6 (3 + -9).
X can also be negative, in that case indexing starts from the end of the list, see examples below.
If x = 0, the sum should be 0 as well.
For example:
print(x_sum_recursion([], 3)) # 0
print(x_sum_recursion([2, 5, 6, 0, 15, 5], 3)) # 11
print(x_sum_recursion([0, 5, 6, -5, -9, 3], 1)) # 0
print(x_sum_recursion([43, 90, 115, 500], -2)) # 158
print(x_sum_recursion([1, 2], -9)) # 0
print(x_sum_recursion([2, 3, 6], 5)) # 0
print(x_sum_recursion([6, 5, 3, 2, 9, 8, 6, 5, 4], 3)) # 15
I have been trying to do this function for 5 hours straight!!!
Would like do see how someone else would solve this.
This is the best i have come up with.
def x_sum_rec_Four(nums: list, x: int) -> int:
if len(nums) == 0:
return 0
elif len(nums) < x:
return 0
elif x > 0:
i = x - 1
return nums[i] + x_sum_rec_Four(nums[i + x:], x)
elif x < 0:
return x_sum_rec_Four(nums[::-1], abs(x))
My problem with this recursion is that the finish return should be:
if len(nums) < x:
return nums[0]
but that would pass things like ([2, 3, 6], 5)) -->> 2 when it should be 0.
If you really need to do it recursively, you could pop x-1 elements from the list before each call, follow the comments below:
def x_sum_recursion(nums, x):
# if x is negative, call the function with positive x and reversed list
if x < 0:
return x_sum_recursion(nums[::-1], abs(x))
# base case for when x is greater than the length of the list
if x > len(nums):
return 0
# otherwise remove the first x-1 items
nums = nums[x-1:]
# sum the first element and remove it from the next call
return nums[0] + x_sum_recursion(nums[1:], x)
print(x_sum_recursion([], 3)) # 0
print(x_sum_recursion([2, 5, 6, 0, 15, 5], 3)) # 11
print(x_sum_recursion([0, 5, 6, -5, -9, 3], 1)) # 0
print(x_sum_recursion([43, 90, 115, 500], -2)) # 158
print(x_sum_recursion([1, 2], -9)) # 0
print(x_sum_recursion([2, 3, 6], 5)) # 0
print(x_sum_recursion([6, 5, 3, 2, 9, 8, 6, 5, 4], 3)) # 15
However, you can do it in a one liner simple and pythonic way:
print(sum(nums[x-1::x] if x > 0 else nums[x::x]))
Explanation:
you slice the list using nums[start:end:increment] when you leave the end empty it slices from the starting position till the end of the list and increments by the increment specified

Range() including its bounds for positive and negative steps

I want to do a simple program that prints all the numbers in a range A to B, including B.
For ranges having bounds in increasing order, I know that I have to add 1 to the upper bound, like:
range(A, B+1)
But adding 1 to B won't work when the bounds are in decreasing order, like range(17, 15, -1).
How can I code it to work for both increasing and decreasing ranges?
I see why you are facing this issue. Its because you are using the larger value as the first argument and smaller value at the second argument in the range (This is happening due to the negative sign).
For such cases following code will work :
a = 5
b = -5
step = 1
if b < 0:
step = -1
range (a, b + step, step)
I think I don't understand the question properly. There are 3 cases:
A, B both positive
A negative, B positive
A, B both negative
Now if I do this (in Python 2, to avoid having to do list(range(...)): this makes the explanation cleaner):
>>> A = 10; B = 20 # case 1
>>> range(A,B+1)
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
>>> A = -10; B = 2 # case 2
>>> range(A,B+1)
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2]
>>> A = -10; B = -2 # case 3
>>> range(A,B+1)
[-10, -9, -8, -7, -6, -5, -4, -3, -2]
So your remark the last number in the range won't be included doesn't seem to fit with what I can see.
If you are receiving input data where A > B, then the problem is not the negative number, but the fact that range() expects the values to be in ascending order.
To cover that:
>>> A = 2; B = -2 # case 4
>>> A,B = sorted((A,B))
>>> range(A,B+1)
[-2, -1, 0, 1, 2]
This also works for cases 1, 2, and 3.
If I have misunderstood the question please edit it to clarify.
Please check if this works. Thank you.
if A>B and A < 0 and B < 0:
print(list(range(A,B,-1)))
elif A<B:
print(list(range(A,B)))
else:
print(list(range(A,B,-1)))
You could create a function that turns a normal range into one that includes both bounds, like:
def inclusive(r):
return range(r.start, r.stop + r.step, r.step)
You should pass it a range, and it will return a range:
increasing_range = range(2, 5)
print(list(inclusive(increasing_range)))
# [2, 3, 4, 5]
decreasing_range = range(5, -5, -1)
print(list(inclusive(decreasing_range)))
# [5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5]
even = range(2, 10, 2)
print(list(inclusive(even)))
# [2, 4, 6, 8, 10]
for odd in inclusive(range(1, 5)):
print(odd)
# 1 2 3 4 5
Last number is never included in python range. You need to adjust the code accordingly.
e.g.
To print values form -5 to -1(included) use,
>>> print(list(range(-5, 0)))
[-5, -4, -3, -2, -1]
In reverse order
>>> print(list(range(-1, -6, -1)))
[-1, -2, -3, -4, -5]

moving elements in a list of Python

I have a list of integers; it contains the integer 27, but we don't know its index.
We have to find the index and then swap it with the item following it. If the index of 27 is the last element (list[-1]) than we swap 27 with the first element.
my_list = [1, 2, 3, 4, 27] # original
my_list = [27, 2, 3, 4, 1] # result
I know how to swap 27 when it is not the last element, but I don't know how to swap 27 with the first element like in the example I showed.
Once the number you are looking for always exists in the list just index and swap using modulo:
my_list = [1, 2, 3, 4, 27]
def swap(l,n):
ln = len(l)
ind = my_list.index(n)
l[ind], l[(ind + 1)% ln] = l[(ind + 1) % ln], l[ind]
Using % ln just mean we wrap around so when ind = 5 as per your example (4 + 1) % 5 will be 0
Output:
In [45]: my_list = [1, 2, 3, 4, 27]
In [46]: swap(my_list, 27)
In [47]: my_list
Out[47]: [27, 2, 3, 4, 1]
In [48]: swap(my_list, 4)
In [49]: my_list
Out[49]: [27, 2, 3, 1, 4]
You might want to handle the case where n does not exist:
def swap(l,n):
ln = len(l)
try:
ind = my_list.index(n)
l[ind], l[(ind + 1)% ln] = l[(ind + 1) % ln], l[ind]
except IndexError:
pass
What you do in the except is up to you.

Summation of only consecutive values in a python array

I am new on python (and even programing!), so I will try to be as clear as I can to explain my question. For you guys it could be easy, but I have not found a satisfactory result on this yet.
Here is the problem:
I have an array with both negative and positive values, say:
x = numpy.array([1, 4, 2, 3, -1, -6, -6, 5, 6, 7, 3, 1, -5, 4, 9, -5, -2, -1, -4])
I would like to sum ONLY the negative values that are continuous, i.e. only sum(-1, -6, -6), sum(-5, -2, -1, -4) and so on. I have tried using numpy.where, as well as numpy.split based on the condition.
For example:
for i in range(len(x)):
if x[i] < 0.:
y[i] = sum(x[i])
However, as you can expect, I just got the summation of all negative values in the array instead. In this case sum(-1, -6, -6, -5, -5, -2, -1, -4)
Could guys share with me an aesthetic and efficient way to solve this problem? I will appreciate any response on this.
Thank you very much
You can use itertools module, here with using groupby you can grouping your items based on those sign then check if it meet the condition in key function so it is contains negative numbers then yield the sum else yield it and at last you can use chain.from_iterable function to chain the result :
>>> from itertools import groupby,tee,chain
>>> def summ_neg(li):
... for k,g in groupby(li,key=lambda i:i<0) :
... if k:
... yield [sum(g)]
... yield g
...
>>> list(chain.from_iterable(summ_neg(x)))
[1, 4, 2, 3, -13, 5, 6, 7, 3, 1, -5, 4, 9, -12]
Or as a more pythonic way use a list comprehension :
list(chain.from_iterable([[sum(g)] if k else list(g) for k,g in groupby(x,key=lambda i:i<0)]))
[1, 4, 2, 3, -13, 5, 6, 7, 3, 1, -5, 4, 9, -12]
Here's a vectorized NumPythonic solution -
# Mask of negative numbers
mask = x<0
# Differentiation between Consecutive mask elements. We would look for
# 1s and -1s to detect rising and falling edges in the mask corresponding
# to the islands of negative numbers.
diffs = np.diff(mask.astype(int))
# Mask with 1s at start of negative islands
start_mask = np.append(True,diffs==1)
# Mask of negative numbers with islands of one isolated negative numbers removed
mask1 = mask & ~(start_mask & np.append(diffs==-1,True))
# ID array for IDing islands of negative numbers
id = (start_mask & mask1).cumsum()
# Finally use bincount to sum elements within their own IDs
out = np.bincount(id[mask1]-1,x[mask1])
You can also use np.convolve to get mask1, like so -
mask1 = np.convolve(mask.astype(int),np.ones(3),'same')>1
You can also get the count of negative numbers in each "island" with a little tweak to existing code -
counts = np.bincount(id[mask1]-1)
Sample run -
In [395]: x
Out[395]:
array([ 1, 4, 2, 3, -1, -6, -6, 5, 6, 7, 3, 1, -5, 4, 9, -5, -2,
-1, -4])
In [396]: out
Out[396]: array([-13., -12.])
In [397]: counts
Out[397]: array([3, 4])
you can flag negative values .... and do this with plain python
prev = False
for i,v in enumerate(a):
j = i + 1
if j < len(a):
if a[i] < 0 and a[j] < 0:
temp.append(v)
prev = True
elif a[i] < 0 and prev:
temp.append(v)
prev = True
elif a[i] > 0:
prev = False
else:
if prev and v < 0:
temp.append(v)
output
print(temp)
[-1, -6, -6, -5, -2, -1, -4]
with intertools i would do just that
def sum_conseq_negative(li):
neglistAll = []
for k, g in groupby(li, key=lambda i:i<0):
negList = list(g)
if k and len(negList) > 1:
neglistAll.extend(negList)
return sum(negList), len(negList)
sumOf, numOf = sum_conseq_negative(li)
print("sum of negatives {} number of summed {}".format(sumOf,numOf))
sum of negatives -25 number of summed 7

Categories