recursive sums for a non_uniform list - python

def _findsum (arr, N):
if len(arr)== 1:
return arr[0]
else:
return arr[0]+_findSum(arr[1:], N)
arr =[]
arr = [1, 2, 3, 4, 5]
N = len(arr)
ans =_findSum(arr,N)
print (ans)
_findsum(arr,N)
OutPut= 15
I need to write a recursive program to sum all elements of a non-uniform nested list.
I need the code to print something similar to the list below.
[7, [4, 6], 2, [1, 3, 5]].

Your question is a little unclear but I'm guessing you need to print the sum of all the numbers present in the parent list. Here's a hint.
In the function, iterate through the list which is passed as a parameter. If the type of the current element is list, call the function with that list as the parameter. If not, return the sum of the elements.

def findsum(arr):
if len(arr) == 0:
return 0
first = arr[0]
rest = arr[1:]
if type(first) == list:
return findsum(first) + findsum(rest)
else:
return first + findsum(rest)
x = [1, [[2, 3], 4]]
print(findsum(x)) # returns 10
If I understood your question correctly, this program should return the sum of the elements in your nested list.
A shorter version looks like this:
def findsum(arr):
return sum((findsum(element) if (type(element) == list) else element) for element in arr)
x = [7, [[2,[-1]]]]
print(findsum(x)) # returns 8

Related

Sort tree which divides list into k parts python

I understand how to sort a list using a binary tree. Eg. sort [ 1,3,5,6,7,3,4,2] from smallest to largest. I recursively split the data into 2 parts each time until it becomes n lists. I then compare 2 lists at a time and append the smaller value into a new list. I do not understand how to do this when it requiress me to splits a list into k parts each time. Eg. k=3. [1,3,5] [6,7,3] [4,2] .I could only find a solution in Java so could someone explain this to me using python?
You have k sublists. At every iteration, find the sublist whose first element is the smallest; append that element to the result list; advance one in that sublist and don't advance in the other sublists.
This is easier if you have a function arg_min or min_with_index that gives you the smallest element as well as its index (so you know which sublist it comes from).
Here are two equivalent ways of writing function min_with_index using python's builtin min to get the min, and enumerate to get the index:
def min_with_index(it):
return min(enumerate(it), key=lambda p:p[1])
import operator
def min_with_index(it):
return min(enumerate(it), key=operator.itemgetter(1))
# >>> min_with_index([14,16,13,15])
# (2, 13)
This was for merging. Here are two different ways of splitting, using list slices:
def split_kway_1(l, k):
return [l[i::k] for i in range(k)]
def split_kway_2(l, k):
j = (len(l)-1) // k + 1
return [l[i:i+j] for i in range(0,len(l),j)]
def split_kway_3(l, k):
j = len(l) // k
result = [l[i:i+j] for i in range(0, j*(k-1), j)]
result.append(l[j*(k-1):])
return result
# >>> split_kway_1(list(range(10)), 3)
# [[0, 3, 6, 9], [1, 4, 7], [2, 5, 8]]
# >>> split_kway_2(list(range(10)), 3)
# [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]]
# >>> split_kway_3(list(range(10)), 3)
# [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]
# versions 2 and 3 differ only when the length of the list is not a multiple of k
And now we can combine splitting and merging to write merge sort:
import operator
def split_kway(l, k):
return [l[i::k] for i in range(k)]
def min_with_index(it):
return min(enumerate(it), key=operator.itemgetter(1))
def merge_kway(list_of_sublists):
result = []
list_of_sublists = [l for l in list_of_sublists if len(l) > 0]
while list_of_sublists:
i,v = min_with_index(l[0] for l in list_of_sublists)
result.append(v)
if len(list_of_sublists[i]) > 1:
list_of_sublists[i].pop(0) # advance in sublist i
else:
list_of_sublists.pop(i) # remove sublist i which is now empty
return result
def merge_sort_kway(l, k):
if len(l) > 1:
list_of_sublists = split_kway(l, k)
list_of_sublists = [merge_sort_kway(l, k) for l in list_of_sublists]
return merge_kway(list_of_sublists)
else:
return list(l)
See also: Wikipedia on k-way merge

How would I write a function that finds all occurrences of a specific value in a list -- continues on

and records--in a separate list--these occurrences in terms of the indices where they were found.
For example find_all([1, 2, 3, 4, 5, 2], 2) returns [1, 5]. find_all([1, 2, 3], 0) returns [ ].
I'm totally new to python and this question is stumping me on my first homework. I need to
take 2 arguments: a list of items, and a single element to search for in the list
returns 1 list: a list of indices into the input list that correspond to elements in the input list that match what we were looking for
not sure
Simple Method using list comprehension
def find_all(l, value):
return [i for i, v in enumerate(l) if v == value]
mylist = [1, 2, 3, 4, 5, 2]
print(find_all(mylist, 2)) # [1, 5]
Use this method since list.index only returns the first match
and you want them all
You can try something like this
def find_all(input_list, search_value):
result = []
for idx, num in enumerate(input_list):
if num == search_value:
result.append(idx)
return result
You should go through the tutorials available on the internet to learn the Python Basics.
https://treyhunner.com/2016/04/how-to-loop-with-indexes-in-python/
https://www.programiz.com/python-programming/function-argument
def find_all(list, num):
newlist = []
for i in range(len(list)):
if (list[i] == num):
newlist.append(i)
return newlist
list = [1, 2, 3, 6, 5, 2]
mylist = find_all(list, 2)
print mylist
Hope this helps.

How do I stop the function when I have a unique list?

I tried a function that would remove both adjacent duplicates in a list. The remove any new duplicate pair and the function will keep going until there are no more duplicate pairs in the list.
I ran into the issue of figuring out how to tell the function to stop once I have a list without adjacent duplicates.
def removepair(no):
i = 1
if len(no) == 0 or len(no) == 1:
return no
while i < len(no):
if no[i] == no[i-1]:
no.pop(i)
no.pop(i-1)
i -= 1
i += 1
return removepair(no)
So far the function will return 0 or single elements after removal:
input: [1, 2, 2, 1] output: []
or
input: [4, 4, 4, 4, 4] output: [4]
But the problem is I don't know how to stop the recursive function once it has a list with more than 1 element:
input: [1,2,3,3,2,1,5,6,7]
expected output: [5,6,7]
We may be able to avoid boolean flags and counters if we set up our recursion carefully:
def removepairs(numbers):
if not numbers: # base case #1, empty
return numbers
first, *second_on = numbers
if not second_on: # base case #2, one element
return numbers
second, *third_on = second_on
if first == second:
return removepairs(third_on)
result = [first] + removepairs(second_on)
if result == numbers:
return numbers # base case #3, no change!
return removepairs(result)
print(removepairs([1, 2, 3, 3, 2, 1, 5, 6, 7]))
OUTPUT
> python3 test.py
[5, 6, 7]
>
If recursive function is not a requirement, it can be simply done using the following code. I have commented the print statement.
def removepair(input_list):
unique_input_list = list(set(input_list))
output_list = list(x for x in unique_input_list if input_list.count(x)%2 == 1)
#print('Input List: ', input_list)
#print('Output list: ', output_list)
return output_list
Input List: [1, 2, 3, 3, 2, 1, 5, 6, 7]
Output list: [5, 6, 7]
Input List: [4, 4, 4, 4, 4]
Output list: [4]
Input List: [1, 2, 3, 3, 2, 1]
Output list: []
Your recursion should stop when no elements where popped from the list, not when the list is almost empty:
def removepair(no):
L = len(no)
if L <= 1:
return no
i = 1
while i < len(no):
if no[i] == no[i-1]:
no.pop(i)
no.pop(i-1)
i -= 1
i += 1
if len(no) < L:
# elements where popped since the list len has decreased
return removepair(no)
else:
return no
Your code is difficult to understand since it uses a mix of recursion and side effects. Usually, you use either one or the other. Here you can replace your recursive call with a while:
def removepair(no):
while True:
L = len(no)
if L <= 1:
return no
i = 1
while i < len(no):
if no[i] == no[i-1]:
no.pop(i)
no.pop(i-1)
i -= 1
i += 1
if len(no) == L: # no elements where popped
return no
But it's not really Pythonic and I think you should not modify the parameter no inside the function but rather return a new list. Why not iterate over the list and do not copy the duplicates in the result?
def removepair(no):
ret = []
for e in no:
if ret and e == ret[-1]: # current element is the same as the last element
ret.pop()
else:
ret.append(e)
return ret
Or with a fold:
def removepair(no):
import functools
return functools.reduce(lambda acc, x: acc[:-1] if acc and acc[-1]==x else acc+[x], no, [])

QuickSort is returning correct values, but not sorting in place

I'm struggling to understand why my QuickSort returns the sorted values correctly, but the resulting array is not sorted correctly.
def qSort(array):
n = len(array)
if (n == 1 or n ==0):
return array
p_index = partition(array)
p_value = array[p_index]
return(qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n]))
def partition(array):
pivot = array[0]
i = 1
for j in xrange(1,len(array)):
print j
if array[j] < pivot:
tmp = array[j]
array[j] = array[i]
array[i]=tmp
i += 1
tmp = array[i-1]
array[i-1] = pivot
array[0] = tmp
return i-1
Here is some sample output:
>>> q = [5,4,3,2,1]
>>> qSort(q)
[1, 2, 3, 4, 5]
>>> q
[1, 4, 3, 2, 5]
Thank you in advance!
In Python, slicing and combining lists create new lists. If you want your recursive calls to operate on a single list in place, pass the list and the bounds into the call, and don't return anything from the function. Something like:
def qsort(array, low, high):
if high-low < 2:
return
# Choose pivot, do partition within bounds
if partition > low:
qsort(array, low, partition)
if partition < high:
qsort(array, partition+1, high)
Then just call qsort(a, 0, len(a)) to sort the array.
This is because you are making up a new list in your return statement.
return(qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n]))
If the qSort function reaches a base case, it returns a list, which is concatenated with [p_value] and returned as a list. You do not make changes to the passed in list anywhere.
When you call your qSort function recursively, you are giving it a slice of the list and the function returns the list in the base case which you then append to the pivot and the other recursive call, hence generating a new list.
See what is happening by changing your qSort function to
def qSort(array):
n = len(array)
if (n == 1 or n ==0):
return array
p_index, array = partition(array)
p_value = array[p_index]
returnVal = qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n])
print "Returning:", returnVal, "Original Array:", array
return returnVal
Output -
>>> q = [5,4,3,2,1]
>>> qSort(q)
Returning: [2, 3] Original Array: [2, 3]
Returning: [2, 3, 4] Original Array: [2, 3, 4]
Returning: [1, 2, 3, 4] Original Array: [1, 4, 3, 2]
Returning: [1, 2, 3, 4, 5] Original Array: [1, 4, 3, 2, 5]
[1, 2, 3, 4, 5]
To reflect the changes in your original list, you have the option of doing q = qSort(q).
P.S - Setting up a random index instead of the first value would be better for your quicksort function. See the bit here on Choice of Pivots.
apply the function back to q
q = qSort(q)
If you want to return the array and also sort in place you should before returning make the array equal to the result and not make a new one. You can do that by changing your return statement to:
array[:] = qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n])
return array
Note that
array = qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n])
does not work either, because the lhs variable will be treated as a local variable.

Function Adding Two Lists

EDIT: Thank you for the responses, which are helpful, but I'm not sure they're getting at the core of my problem. In some cases it would be nice to simply select the lower of the lists and then add the values accordingly, but in THIS case I actually want to treat uneven lists as though the shorter list had zero values for the values it was missing. So I want [1, 2, 3] + [1, 2] to function as [1, 2, 3] + [1, 2, 0]. I don't think zip, or reversing my operator, will accomplish this.
I'm trying to create a function that adds the corresponding values of two lists and returns a new list with the sums of each of the indices of the two original lists:
def addVectors(v1, v2):
print(v1[0], v1[1], v2[0])
newVector = []
if len(v1) > len(v2):
for index in range(len(v1)):
print(index)
newVector[index] += v1[index] + v2[index]
else:
for index in range(len(v2)):
print(index)
newVector[index] += v2[index] + v1[index]
return newVector
addVectors([1, 2, 3], [1, 2])
Yet I'm getting an error stating that the list index is out of range? Not sure what I'm doing wrong in this seemingly simple program....
You probably meant to change the line:
if len(v1) > len(v2):
to:
if len(v1) < len(v2):
That way, you are iterating to the number of elements in v1 when v1 is shorter, which prevents you from going over the edge.
Note that this would also throw an error because newVector is a list of length 0 and you are accessing outside of its range. You'd have to change
newVector[index] += v1[index] + v2[index]
to
newVector.append(v1[index] + v2[index])
However, note that this can be done much more simply as:
def addVectors(v1, v2):
return map(sum, zip(v1, v2))
ETA: To pad the list with zeros, do:
import itertools
def addVectors(v1, v2):
return map(sum, itertools.izip_longest(v1, v2, fillvalue=0))
For example:
addVectors([1, 2, 3, 4, 5], [1, 2])
# [2, 4, 3, 4, 5]
Why not just use this?
def sum_lists(a, b):
return [x[0] + x[1] for x in zip(a, b)]
sum_lists([1, 2, 3], [4, 5, 6]) # -> [5, 7, 9]
You compare the lengths of the list, which is correct. But then you change the required operations. I.e. when list1 is longer than list2, you should only loop over the elements for the length of list2.
To fill the lists of uneven length, you can use itertools:
>>> import itertools
>>> map(sum, itertools.izip_longest([1,2,3], [1,2], fillvalue = 0))
[2, 4, 3]
Your problem lies here:
if len(v1) > len(v2):
for index in range(len(v1)):
print(index)
newVector[index] += v1[index] + v2[index]
You ensure that len(v1) > len(v2), but then iterate over range(len(v1)).
In your example, you're trying to access v2[2], which doesn't exist.
UPDATE:
In response to your edit, you could use something like this:
def addVectors(v1, v2):
if len(v1) > len(v2):
map(sum, zip(v1, v2)).extend(v1[len(v2):])
else:
map(sum, zip(v1, v2)).extend(v2[len(v1):])
Your IndexError is because you're trying to write to newVector[index] when newVector is an empty list. You need to either initialize it to a bunch of zeros, or use append instead.
>>> first = [1,2,3]
>>> second = [1,2]
>>> output = []
>>> for i, item in enumerate(first):
... additional = second[i] if i < len(second) else 0
... output.append(item + additional)
...
>>> output
[2, 4, 3]
And to ensure that len(first) > len(second), you can do something like this:
first, second = first, second if len(first) > len(second) else second, first
Or you can try
def add_vector(vector1, vector2):
index = len(vector1) - 1
new = []
while index >= 0:
result = vector1[index] + vector2[index]
new.append(result)
index -=1
new.reverse()
return new

Categories

Resources