I'm new to python and trying to solve my homework... I'm trying to create a recursion function that takes a list of numbers [a, b, c....] and turns it to this list: [a, a+b, a+b+c, ....].
This is my code:
def rec_cumsum(numbers):
''' Input: numbers - a list of numbers,
Output: a list of cumulative sums of the numbers'''
new_list=numbers
last=new_list[-1]
if numbers==[]:
return numbers
if len(numbers) == 1:
return numbers[0]
new_list.remove(last)
rec= rec_cumsum(new_list)
new_list.append(rec+last)
return last+rec
this works but because I used return for last+rec, I can't use return to get the list back (new_list). Please explain to me what did I do wrong... thanks!
Let's write some test cases and practice some test-driven development:
tests = [[], # Desired answer: []
[1], # [1]
[1,2], # [1, 3]
[1,2,3], # [1, 3, 6]
[1,2,1,3]] # [1, 3, 4, 7]
for t in tests:
print(rec_cumsum(t))
If we add this to your code and run it, we get:
last=new_list[-1]
IndexError: list index out of range
Why is this? Apparently -1 is an out-of-range index. Why wouldn't new_list have a -1 index?
Aha. That happens if new_list is empty. So we need to address the base case first. While we're at it, let's also use #MartijnPieters' suggestion:
if len(numbers) <= 1:
return numbers
to obtain
def rec_cumsum(numbers):
''' Input: numbers - a list of numbers,
Output: a list of cumulative sums of the numbers'''
if len(numbers) <= 1:
return numbers
new_list=numbers
last=new_list[-1]
new_list.remove(last)
rec = rec_cumsum(new_list)
new_list.append(rec[-1]+last)
return last+rec
Now run the test again. This time we get
return last+rec
TypeError: unsupported operand type(s) for +: 'int' and 'list'
So now Python is saying last is an int and rec is a list, and we can not add the two together.
Okay, rec should be a list since it is the return value of rec_cumsum(new_list). What should replace last+rec?
Let's think in terms of a concrete example. If rec is [a, a+b] then we want to return [a, a+b, a+b+c]. How do we form a+b+c?
How about adding the last element in rec with the last element of numbers:
rec[-1]+last
We want to append this to the end of rec:
rec.append(rec[-1]+last)
Let's make that change and see what happens. But while we're editing, let's also clean up some code we never use. We can delete new_list.append(rec[-1]+last):
def rec_cumsum(numbers):
''' Input: numbers - a list of numbers,
Output: a list of cumulative sums of the numbers'''
if len(numbers) <= 1:
return numbers
new_list=numbers
last=new_list[-1]
new_list.remove(last)
rec = rec_cumsum(new_list)
rec.append(rec[-1]+last)
return rec
Now our program returns
[]
[1]
[1, 3]
[1, 3, 6]
[2, 3, 4, 7]
Hurray, our program runs without errors. But wait... it returns the wrong results. Look at the last line.
rec_cumsum([1,2,1,3]) is returning [2,3,4,7] whereas the correct answer is [1,3,4,7]. Why is the first number wrong?
It has to do with new_list.remove(last). This command removes the first occurrence of last from new_list. We want to remove the last occurrence.
So instead, let's use
new_list = numbers[:-1]
So the program becomes:
def rec_cumsum(numbers):
''' Input: numbers - a list of numbers,
Output: a list of cumulative sums of the numbers'''
if len(numbers) <= 1:
return numbers
new_list=numbers[:-1]
last=numbers[-1]
rec = rec_cumsum(new_list)
rec.append(rec[-1]+last)
return rec
Run the tests. It works! Now it is a good practice to look back at our solution and see how it can be tightened up and made more elegant.
I see we used new_list and last as temporary variables. Do we really need them?
def rec_cumsum(numbers):
if len(numbers)<=1:
return numbers
result = rec_cumsum(numbers[:-1])
result.append(result[-1]+numbers[-1])
return result
Your function should always return a list; for the empty list case, an empty list, and for the case where there is only one element, a list with only one element.
But your code returns the one element, not a list with one element:
if len(numbers) == 1:
return numbers[0]
Change that to returning just numbers:
if len(numbers) == 1:
return numbers
You can combine this with the other end-state test:
if len(numbers) < 2:
return numbers
Next problem is that you are not creating a copy of a list when you create the variable new_list; you create a reference to the same list, you'd have to use a slice or create an explicit new list:
new_list = numbers[:]
If you are going to remove a value from that list anyway, you may as well adjust the slice a little, and put this after testing numbers (why do the work otherwise):
if len(numbers) < 2:
return numbers
new_list = numbers[:-1]
last = numbers[-1]
Nowhere in your code do you actually compute a sum; nothing is added to your numbers. You never add a to b, c, etc. Moreover, you seem to be focusing on the last number, while your assignment states you needed to sum the first value to the rest of the list.
And there is a pattern there. Not only do you add a to b, but the sum of a + b is added to c, and that sum is then added to d, etc. Let's make use of that:
def rec_cumsum(numbers, culmulated_sum=0):
if len(numbers) < 1:
return numbers
sum = numbers[0] + culmulated_sum
return [sum] + rec_cumsum(numbers[1:], sum)
Note that I don't even bother storing a copy of numbers now; may as well just pass it to the next recursion as a slice of everything but the first element. We also use the first element from numbers to create our sum (numbers[0]).
Also, we now pass along the culmulative sum so far, starting at 0; and that means we need to change the end condition of the recursive function; we are essentially adding [0] to the start of the list, and we want to make sure we sum that with the next element always.
This now does what you need:
>>> rec_cumsum([5, 1, 10, 2, 3])
[5, 6, 16, 18, 21]
Related
Hello i want to ask how to loop through each element and increment every time by one this is what i want first i want to sum 0 , 1, 3 ,6 , 10 and after that sum can somebody help me about that i don't know how to tell if it is loop through each element or iterate.It should look like these examples.I am sorry!
ls = [0, 1, 3, 6, 10]
ls = [1, 3, 6, 10]
ls = [3, 6, 10]
ls = [6, 10]
ls = [10]
ls = []
Here's the problem who i want to solve it :
https://www.codewars.com/kata/5ce399e0047a45001c853c2b/train/python
I tried this but it doesn't work
def parts_sums(ls):
length_list = len(ls)
for i in range(0,length_list+1):
return length_list
Note that there is a built-in function sum() in Python that does that job probably better than any code you can write in Python.
sum([0, 1, 3, 6, 10])
However, if you want to practice writing your sum function by iterating through a list and summing all the elements, this is how you do it.
def my_sum(ls):
result = 0
for i in range(len(ls)):
result += ls[i]
return result
First of all, you need to initialize a variable to hold your result. The range() function generates all values from 0 to x. The for-loop assigns all values generated by the range function to i in order and executes the indented block below. The += assignment increments the left-hand side variable by the right-hand side expression value. At last, we return the result.
And if you prefer using a while-loop,
def my_sum(ls):
result = 0
i = 0
while i < len(ls):
result += ls[i]
i += 1
return result
It's always good to consult Python documentation when you are not sure how to use its built-in function.
If you want the accumulated sum of all items reversed, you can take a look at the accumulate function in itertools.
from itertools import accumulate
def parts_sums(ls):
return list(accumulate(ls[::-1]))[::-1] + [0]
Or if you want to implement with a loop,
def parts_sums(ls):
result = []
part_sum = 0
for item in ls[::-1]:
result.append(part_sum)
part_sum += item
result.append(part_sum)
return result[::-1]
Or if you want to do it without reversing the list (say if you want to yield the results)
def parts_sums(ls):
result = []
part_sum = sum(ls)
for item in ls:
result.append(part_sum)
part_sum -= item
result.append(part_sum)
return result
Note the algorithm is still O(n), not that time complexity matters in this case.
For the question you have mentioned in codewars, you need to loop it thro 2 loops and keep reducing the first element in the inner loop for sum.
def parts_sums(ls):
# your code
sum = []
for i in range(len(ls)):
sum_temp =0
for j in range(i,len(ls)):
sum_temp += ls[j]
sum.append(sum_temp)
sum.append(0) # for the final empty list
return sum
print(parts_sums([0, 1, 3, 6, 10]))
This test will check the execution time too. So you need to be fast.
Naive approach
You can use sum or create your own sum.
def parts_sums(ls):
return [
sum(ls[i:])
for i in range(len(ls) + 1)
]
But i means you'll need to loop in a list twice. So it will be slow.
Sneaky approach
In a list like [a, b, c, d, e] you are calculating:
[a+b+c+d+e, a+b+c+d, a+b+c, a+b, a, 0]. So let's start from last to first element. [0, a, a+b, a+b+c, a+b+c+d, a+b+c+d+e]. Now we see a cumulative iteration:
So get loop in the list, get the element, sum it with last element of the result ([0]) list and add it as the last element to the result list. Lastly reverse the result.
def parts_sums(ls):
res = [0]
for i in range(len(ls)-1, -1, -1):
res.append(res[-1] + ls[i])
return res[::-1]
I have written a code and run it, and it works fine. But I wanted to understand what is happening in the following:
nums = [4, 5, 1, 8]
target = 12
def TwoSum(nums, target):
comps = dict()
for i in range(len(nums)):
comp = target - nums[i]
if nums[i] in comps:
return [comps[nums[i]], i]
else:
comps[comp] = i
print(TwoSum(nums, target))
I understand that this is using a dict, and the idea is to add elements from nums to it, then doing target - nums[i] and then checking if this is in the dict, and if it is, then returning the indices of the two numbers that sum to the target.
But how is comps = dict() used? Is it necessary? Because in the code it doesn't seem to be storing anything! Except for the last line it is used- but I don't understand what it does- can someone please explain?
First, your code was using self as first argument of TwoSum. It should be eliminated given that this is a static function, not a class method. (Fixed).
The line comp = dict() is an initialization of comp to an empty dict. It could be written in a more pythonic way: comp = {}.
comp appears to store the complement (the difference between target and nums[i]) as comps[diff] = i. Thereafter, when you examine a number nums[j], if the complement is already in comps, that j and the corresponding previous i is the pair of indices you are looking for.
In our case, it stores number as a key thats required to find a target, and index of opposit number in nums as value.
So, for example, when we have target at 12 and nums [4,5,1,8] , at the iteration on the number 4, 8->0 will be added to comps, when iteration will get to the number 8, we will check if we have number 8 in comps, and then return value of that number in comps, which is 0, and index of current iterating number, which is 3.
The positive_numbers function should return a list containing numbers that are non-negative (>=0).
However, currently, it returns only the [3] instead of a list of values.
I tried different ways to make this work I'm currently a beginner in python and I don't have anyone to ask that's why I'm sorry if this is too basic for you just want to learn and see my mistake
def positive_numbers(numbers):
for number in numbers:
result = []
result = result + [number]
if number < 0:
continue
return result
Expected: The function should return a list of numbers
Actual: returns list with the only value being 3
Error: The call positive_numbers([1, 2, 3]) should return [1, 2, 3], but it returned [3]
You reset the list each iteration of the loop because of result = [].
Try this:
def positive_numbers(numbers):
result = []
for number in numbers:
if number > 0:
result = result + [number]
return result
Also, debugging would make this very clear. Try to debug your code to get a better understanding of it.
You have to declare result outside of your for loop.
def positive_numbers(numbers):
result = []
for number in numbers:
if number < 0:
continue
result.append(number)
return result
As already stated, you are setting results no a new empty string at every iteration of your for loop. Also, the if statement does not make any change, since by the time is evaluated, the new number is appended to the result.
So the minimal working change is:
def positive_numbers(numbers):
result = []
for number in numbers:
if number 0:
continue
result = result + [number]
return result
Besides, note that you could use a list comprehension for that:
positive_numbers = [number for number in numbers if number >= 0]
Or using filter:
positive_numbers = list(filter(lambda num: num>=0, numbers))
Note that in the latter case, if you don't need your output to be a list but an iterable, you can remove the list(...) part
You have all of the pieces, just not necessarily in the right order. Here is an amended version:
def positive_numbers(numbers):
result = []
for number in numbers:
if number < 0:
continue
result = result + [number]
return result
With result = [] inside the for loop as you had it, you were emptying the result on each iteration. Then you were unconditionally adding the number to the list instead of checking if it is negative first. In this amended first, the loop skips to the next number if the current number is not non-negative.
I am trying to write a recursive function which takes as its inputs an integer n, and a list l, and returns a list of all the combinations of size n that can be chosen from the elements in l. I'm aware that I can just use itertools, but I want to become better at writing recursive functions, and I believe writing my own function will help me.
So for example, if you input:
n = 3
l = [1, 2, 3, 4]
I want the output to be:
`[ [1, 2, 3], [1, 3, 4], [2, 3, 4], [1, 2, 4] ]
So far I've written this code:
def get_combinations(l, n): # returns the possible combinations of size n from a list of chars
if len(l) == 0:
return []
elif n == 1:
return [l[0]]
newList = list()
for i in range(len(l)):
sliced_list = l[i:]
m = n - 1
#lost here, believe I need to make a recursive call somewhere with get_combinations(sliced_list, m)
return newList
I found this example of such a function for permutations helpful, but I'm struggling to implement something similar.
To clarify, I set up my base cases in the way I did because I'm expecting to pass sliced_list and m in my recursive call, and if you imagine the situation when i = 3, you'll have an empty list for sliced_list, and m will be 1 when you've gone deep enough to build up a combination. But I'm not married to these base cases.
Let me try to summarize the questions I have:
How do I produce a final result which is exactly a list of lists, instead of a list of lists of lists of lists ... (depth = n)?
What should my recursive call look like?
Am I going about this problem in exactly the wrong way?
First I would recommend that you layout your function like:
def get_combinations(array, n):
solutions = []
# all the code goes here
return solutions
That way if there's a case that's a problem, like n == 0, you can just ignore it and get a valid (empty) result. The next issue is this line is wrong:
elif n == 1:
return [array[0]]
The correct thing to do if n == 1 is to return an array with every element of the array wrapped in a list:
if n == 1:
solutions = [[element] for element in array]
This is your base case as the next layer up in the recursion will build on this. What comes next is the heart of the problem. If n > 1 and the array has content, then we need to loop over the indexes of the array. For each we'll call ourself recursively on everything past the current index and n - 1:
sub_solutions = get_combinations(array[index + 1:], n - 1)
This returns partial solutions. We need to stuff the element at the current index, ie. array[index], onto the front of each sub_solution in sub_solutions, and add each augmented sub_solution to our list of solutions that we return at the end of the function:
solutions.append([array[index]] + sub_solution)
And that's it!
I need to write a program that compute cumulative sums from a list of numbers with def but ONLY with recursion.
I did it, but now I need to write the same program without using the method sum, but no success so far.
Any idea?
my code:
def rec_cumsum(numbers):
''' Input: numbers - a list of numbers,
Output: a list of cumulative sums of the numbers'''
if len(numbers)==0: return numbers
return rec_cumsum(numbers[:-1])+ [sum(numbers)]
input:
1 [1,2,3]
2 [2, 2, 2, 3]
output:
1 [1,3,6]
2 [2, 4, 6, 9]
my code without sum:
def rec_cumsum(numbers):
''' Input: numbers - a list of numbers,
Output: a list of cumulative sums of the numbers'''
if len(numbers) == 0: return numbers
my_list=[]
rec_cumsum(my_list + numbers)
my_list[0]=numbers[0]
rec_cumsum(my_list)
temp_sum=my_list[0]+numbers[-1]
my_list[0]=temp_sum
return my_list
I would suggest something like this without adding additional arguments:
[UPDATED]
def rec(n):
if len(n) < 2: return n
n[1] = n[0] + n[1]
return [n[0]] + rec(n[1:])
print rec([1,2,3,4])
[1, 3, 6, 10]
What you can do is: -
Create a temp list(an empty one).
Pass your original list and the empty list in your method.
Now, when you pass your list for the first time, just add the first element from your original list to it. And call the same method with the rest of the list. Starting from 1st element.
When your method is invoked after wards, you need to take a sum of last element of your temp list and first element of the original list that is now modified. And add the sum to your temp list as new element.
Finally, when the length of your original list becomes 0. Return your temp.
**Here's the code for the above steps. You can compare it with the one you have implemented, and see where you went wrong: -
def rec_cumsum(numbers):
if len(numbers) == 0 : return temp
# You need to check, if `temp` is empty, that means method is called first time.
if not temp:
temp.extend([numbers[0]]) // Just add the first element to it.
else:
# Else, get the last element from `temp`,
# add it to `first elemt` in `numbers` and add it to `temp`.
temp.extend([temp[-1] + numbers[0]])
return rec_cumsum(numbers[1:])
my_list = [2, 2, 2, 3]
temp = []
print rec_cumsum(my_list)
yet another solution would be:
def rec(n):
if len(n) < 2: return n
rest = rec(n[:-1])
return rest + [rest[-1] + n[-1]]
this one feels more intuitive to me..