Finding all consecutive indices that sum X number on list - python

I'm currently practicing some Python and came across this problem. Let's say we have a list of integers and we want to find out all the indices of its elements that sum a certain number (in particular, the first index and the last index). Here's an example:
arr = [6, 7, 5, 4, 3, 1, 2, 3, 5, 6, 7, 9, 0, 0, 1, 2, 4, 1, 2, 3, 5, 1, 2]
sum_to_find = 13
So we have the array, and we want to find all elements that sum 13, and save the indexes (first and last of the interval) each time. The answer for this problem would be:
answer = [[0, 1], [2, 5], [3, 7], [9, 10], [12, 19], [13, 19], [14, 19], [18, 22]]
Below is the code I've tried:
def find_sum_range(array):
summ = 0
lst_sum = []
lst_ix = []
i = 0
j = -1
while i < len(array):
if summ < 13:
val = array[i]
summ += val
lst_ix.append(i)
elif summ == 13:
lst_sum.append(lst_ix)
j += 1
i = lst_sum[j][1]
summ = 0
lst_ix = []
i += 1
return lst_sum
But it's only returning the first two answers, mostly because I can't seem to properly backtrack the i iterator to start again from the first index of the last sum it correctly identified.

This approach is unnecessarily complicated. Utilizing list slicing produces much simpler code. Try this:
def find_sum_range(array):
result = []
for begin in range(len(array)):
for end in range(begin, len(array)):
if sum(array[begin:end+1]) == 13:
result.append([begin,end])
return result
or, with list comprehension:
def find_sum_range(array):
return [ [begin,end]
for begin in range(len(array))
for end in range(begin+1, len(array))
if sum(array[begin:end+1]) == 13 ]

A simple approach to this problem would be to use nested loops. So, go over each element in the list and for each of them iterate over the elements in the list that come after that element.
As soon as the sum exceeds or is equal to summ, we can break the nested for loop and go over to the main loop. If it turns out to be equal, then just append a list with the correct indices to the answer.
arr = [6, 7, 5, 4, 3, 1, 2, 3, 5, 6, 7, 9, 0, 0, 1, 2, 4, 1, 2, 3, 5, 1, 2]
req_sum = 13
answer = []
for i in range(len(arr)):
curr_s = arr[i]
for k in range(i+1, len(arr)):
curr_s += arr[k]
if curr_s >= req_sum:
if curr_s == req_sum:
answer.append([i, k])
break
print(answer)

Note that you use i = lst_sum[j][1] to try to backtrack. This is the second element in the list you just saved. You should use i = lst_sum[j][0] instead.
Also, you need to treat the case where you go above 13.
You can reduce the number of operations needed by moving a start index instead of keeping all the potential list indexes and deleting everything each time you arrive at 13 or above:
def find_sum_range(array):
summ = 0
lst_sum = []
start = 0
end = -1
for element in array:
summ += element
end += 1
while summ >= 13:
if summ == 13:
lst_sum.append([start, end])
summ -= array[start]
start += 1
return lst_sum

Related

Split an array according to user defined rules

I am new to Python and I am quite struggling with how to split the array according to user-defined rules.
Say I have an array with elements inside:
A=[0,1,2,6,7,8,9,14,15,16]
I want to use python to split the above array into several groups.
So if the interval between the current element and the next element is smaller than 2, then they belong to one group, else belong to a different group.
So essentially I want my new array to look like this:
B = [[0,1,2] [6,7,8,9] [14,15,16]]
I was thinking to use for loop and loop through array A, but not quite sure how to do so....
A basic for loop works
a=[0,1,2,6,7,8,9,14,15,16]
g = []
tmp = []
for i in range(len(a)):
tmp.append(a[i])
if i==len(a)-1 or a[i+1] - a[i] >= 2:
g.append(tmp)
tmp = []
print(g)
Output
[[0, 1, 2], [6, 7, 8, 9], [14, 15, 16]]
You can just iterate over your A list, starting a new sub-list in B each time the difference between the current value in A and the previous one is more than 1:
A=[0,1,2,6,7,8,9,14,15,16]
B = [[A[0]]]
Bindex = 0
last = A[0]
for i in range(1, len(A)):
if A[i] - last >= 2:
B.append([A[i]])
Bindex += 1
else:
B[Bindex].append(A[i])
last = A[i]
print(B)
Output:
[[0, 1, 2], [6, 7, 8, 9], [14, 15, 16]]
A = [0, 1, 2, 6, 7, 8, 9, 14, 15, 17]
temp = []
B = []
for index, value in enumerate(A):
if index != 0 and value - A[index-1] >= 2:
B.append(temp)
temp = [value]
continue
temp.append(value)
B.append(temp)
How the code works:
Enumerate takes a list and makes it into tuple format.
So enumerate will make your list look like this:
(0, 0) (1, 1) (2, 2) (3, 6) (4, 7) etc....
index != 0 condition ensures that you do not do access an element before 0 in the next part of the if statement. I.e. A[index-1], if index was 0 then this would be A[0-1] = A[-1] = NOT what we want (because this will give the last element)
temp = [value] makes a new list. I.e. the next list to be appended into B.
continue goes back to the for loop skipping any code below it for that particular iteration.
I think an elegant and much faster way is to use itertools.groupby like:
from itertools import groupby
from operator import itemgetter
A=[0,1,2,6,7,8,9,14,15,16]
B = [list(map(itemgetter(1), group)) for key, group in groupby(enumerate(A), lambda x: x[0] - x[1])]
A = [0, 1, 2, 6, 7, 8, 9, 14, 15, 17]
B = []
start = 0
end = 0
for i, val in enumerate(A):
if i != 0 and val - A[i-1] >= 2:
B.append(A[start:end])
start = end
end += 1
B.append(A[start:end])

How to create an output list which is the sum of alternating elements from an input number_list

I'm trying to sum the even and odd-indexed elements of a list together without the use of the sum() method, but something is going wrong. I'm new to Python so help would be appreciated.
An example would be input number_list as [1, 2, 3, 4] then the output list will be [4, 6], which is from the sum of the even-numbered elements 1 and 3, and the odd-numbered elements 2 and 4 from the list number_list. Similarly, [5, 8, 3, 2, 6, 7, 1] would have an output list of [8, 10, 9, 9, 5].
This is my code:
number_list = [2, 6, 3, 5, 9, 1, 7]
res=[0, 0, 0, 0, 0, 0]
for i in range(0, len(number_list)):
if(i % 2):
res[1] += number_list[i]
else :
res[0] += number_list[i]
print("Input List: {0}\n".format(number_list))
print("Output List: {0}\n".format(res))
I think the following is what you were going for; your first attempt was definitely on target:
!/usr/bin/env python3
number_list = [2, 6, 3, 5, 9, 1, 7]
i = 0
r = [0,0]
for n in number_list:
if (i % 2) == 0:
# Even
r[0] += n
else :
# Odd
r[1] += n
i += 1
print(" Input List: {0}". format(number_list))
print("Output List: {0}". format(r))
The result is:
Input List: [2, 6, 3, 5, 9, 1, 7]
Output List: [21, 12]
There are some simplifications here, namely throwing out Python nuances and just looking at iterating over a list of numbers, determining whether they are in the even or odd position (the i index), and adding (summing) them to the base r array. Is this the most compact and optimized Python code you will encounter? No. But is it clear as to what you're trying to do - I think so.
Joe's answer is absolutely correct. However more compact answer would be..
r = [0,0]
for n, val in enumerate(number_list):
j = n % 2
r[j] += val
print(" Input List: {0}". format(number_list))
print("Output List: {0}". format(r))

Python create multi dimensional array of of normal array

I want to create a multi dimensional Array out of normal array. When there is a 0 in my start array i want to change the row number by 1.
My start array looks like this:
arr = [1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9]
My futre multi array should look like this:
multi_arr = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
My code looks like this so far:
multi_arr = []
end = len(arr)
i = 0 #row
j = 0 #column
for h in range(end):
if arr[h] != 0:
j += 1
multi_arr[i][j]= arr[h]
elif arr[i] != 0:
i += 1
I always get a list index error with this code.
If you really want to work with lists, you dont need a column indexer. You can simply append the value to the correct list.
multi_arr = [[]] # start with a nested list, since your vector doesnt start with a 0
endi = len(arr)
i = 0 #row
for h in range(endi):
if arr[h] != 0:
multi_arr[i].append(arr[h])
else:
i += 1
multi_arr.insert(i,[]) # open new list
output:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
This should do the job in a simpler way:
arr = [1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9]
multi_arr = []
temp_arr = []
for h in arr:
if h != 0:
temp_arr.append(h)
else:
multi_arr.append(temp_arr)
temp_arr = []
if h != 0: # Insert the last array it the last number wasn't 0
multi_arr.append(temp_arr)
print(multi_arr)
This is happening because at the point where you are trying set multi_arr[i][j], it is still a 1-d array. You can verify this with a print statement or a debugger - like so:
for h in range(end):
if arr[h] != 0:
print(multi_arr)
j += 1
multi_arr[i][j]= arr[h]
elif arr[i] != 0:
i += 1
I would definitely try something more pythonic for what you are trying to accomplish, reading the elements into a temp array and appending that temp array to multi array.
arr = [1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9]
multi_arr = []
temp_array = []
for item in arr:
if item == 0:
multi_arr.append(temp_array)
temp_array = []
else:
temp_array.append(item)
multi_arr.append(temp_array)
print(multi_arr)
You are declaring a 1D array and appending like 2D array
One solution two your Problem is :
arr=[1,2,3,4,5,6,7,8,9]
multi_arr=[]
end = len(arr)
a = []
counter =0
for i in arr :
if counter < 3 :
a.append(i)
counter += 1
else:
multi_arr.append(a)
a=[]
counter = 0
a.append(i)
counter += 1
multi_arr.append(a)
print(multi_arr)
Here is a yield approach:
def f(arr):
res = []
for x in arr:
if x == 0:
yield res
res = []
else:
res.append(x)
yield res
list(f(arr))
#> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
A different approach for you: super simple and no loop.
import numpy as np
arr = [1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9]
a = [i for i in arr if i]
x = arr[arr!=0] + 1
np.array(a).reshape([-1, x])
Output:
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
And if you need lists, just use np.array(a).reshape([x, y]).tolist() instead.
One-liner:
After converting your list to a numpy array a = np.array(arr):
a[a!=0].reshape([-1, len(a) - np.count_nonzero(a)+1])

I need the sum of numbers from a for-loop that is NOT in a list in Python

I'm having a problem returning the sum. I keep getting zero.
I commented the print results for better understanding. Again, my code is returning 0, and not 11.
A = [1, 5, 2, 1, 4, 0]
def solution(A):
start = [i - j for i, j in enumerate(A)]
start.sort() #[-4, -1, 0, 0, 2, 5]
for i in range(0, len(A)):
pair = 0
end = i + A[i] #1, 6, 4, 4, 8, 5
count = bisect_right(start, end) #4, 6, 5, 5, 6, 6
count_1 = count-1 #3, 5, 4, 4, 5, 5
count_2 = count_1 - i #3, 4, 2, 1, 1, 0
pair += count_2 #??????? I need the above added together like this 3+4+2+1+1+0 since
that's the answer.
return pair
print(solution(A))
As you've written in the comments, the last count2 is zero.
You're adding zero to zero, the loop ends, and you return zero.
You need to start the counter outside the loop, or you can sum outside too, like so
counts = []
for i, x in enumerate(A):
counts.append(bisect_right(start, i + x) - 1 - i)
return sum(counts)
Which could be rewritten
return sum(bisect_right(start, i + x) - 1 - i for for i, x in enumerate(A))

Can this merge sort be more efficient?

I made a merge sort in python just to get a better understanding of how it works. The code works fine, but I'm wondering if anyone can critique what I've written. Can this be more efficient?
I anticipate that there is some room for improvement in my recursion - the function continues running after it calls and runs itself. I've prevented it from continuing unnecessarily by simply giving it a return immediately after a recursive call, but I am not sure if there is a better method.
Also, I have to specify in the output that I want index 0 of the returned value, as the merging section of the function generates a multidimensional array.
"""
2019-10-17
This project simply re-creates common sorting algorithms
for the purpose of becoming more intimately familiar with
them.
"""
import random
import math
#Generate random array
array = [random.randint(0, 100) for i in range(random.randint(5, 100))]
print(array)
#Merge Sort
def merge(array, split = True):
split_array = []
#When the function is recursively called by the merging section, the split will be skipped
continue_splitting = False
if split == True:
#Split the array in half
for each in array:
if len(each) > 1:
split_array.append(each[:len(each)//2])
split_array.append(each[len(each)//2:])
continue_splitting = True
else:
split_array.append(each)
if continue_splitting == True:
sorted_array = merge(split_array)
#A return is set here to prevent the recursion from proceeding once the array is properly sorted
return sorted_array
else:
sorted_array = []
if len(array) != 1:
#Merge the array
for i in range(0, len(array), 2):
#Pointers are used to check each element of the two mergin arrays
pointer_a = 0
pointer_b = 0
#The temp array is used to prevent the sorted array from being corrupted by the sorting loop
temp = []
if i < len(array) - 1:
#Loop to merge the array
while pointer_a < len(array[i]) or pointer_b < len(array[i+1]):
if pointer_a < len(array[i]) and pointer_b < len(array[i+1]):
if array[i][pointer_a] <= array[i + 1][pointer_b]:
temp.append(array[i][pointer_a])
pointer_a += 1
elif array[i + 1][pointer_b] < array[i][pointer_a]:
temp.append(array[i + 1][pointer_b])
pointer_b += 1
#If the pointer is equal to the length of the sub array, the other sub array will just be added fully
elif pointer_a < len(array[i]):
for x in range(pointer_a, len(array[i])):
temp.append(array[i][x])
pointer_a += 1
elif pointer_b < len(array[i + 1]):
for x in range(pointer_b, len(array[i + 1])):
temp.append(array[i + 1][x])
pointer_b += 1
else:
for each in array[i]:
temp.append(each)
sorted_array.append(temp)
if len(sorted_array) != 1:
#Recursively sort the sub arrays
sorted_array = merge(sorted_array, False)
return sorted_array
sorted_array = merge([array])[0]
print()
print(sorted_array)
To make it more efficient, you simply can use sum() and sorted() to merge and sort the lists:
list1 = [[2, 7, 10], [0, 4, 6], [3, 11]]
list2 = [[2, 7, 10], [0, 4, 6], [3, 11, 1]]
list3 = [[10, 567, 34], [-10, -30, -109], [98, 10001, 5]]
def merge_sort(lists, ascending=True):
if ascending == True: # If in an ascending order
return sorted(sum(lists,[])) # `sum()` merges and `sorted()` sorts it
elif ascending == False: # If in a descending order
return sorted(sum(lists,[]), reverse=True) # `reverse=True` makes it descending
>>> print(merge_sort(list1))
print(merge_sort(list2))
print(merge_sort(list3))
[0, 2, 3, 4, 6, 7, 10, 11]
[0, 1, 2, 3, 4, 6, 7, 10, 11]
[-109, -30, -10, 5, 10, 34, 98, 567, 10001]
>>> print(merge_sort(list1, ascending=False))
print(merge_sort(list2, ascending=False))
print(merge_sort(list3, ascending=False))
[11, 10, 7, 6, 4, 3, 2, 0]
[11, 10, 7, 6, 4, 3, 2, 1, 0]
[10001, 567, 98, 34, 10, 5, -10, -30, -109]

Categories

Resources