Related
I'm new to dynamic programming in python and this is the code I have done so far to get the numbers that give the max sum from the array. However, my code doesn't work for input array A
Here are cases:
Test cases:
A = [7,2,-3,5,-4,8,6,3,1]
B = [7,2,5,8,6]
C = [-2,3,1,10,3,-7]
Output:
A = [7,5,8,3]
B = [7,5,6]
C = [3,10]
My output works for B and C but not for array A. The output I get is this:
[7,6,1]
And Here is my code:
def max_sum(nums):
#Get the size of the array
size = len(nums)
list = []
cache = [[0 for i in range(3)] for j in range(size)]
if(size == 0):
return 0
if (size == 1):
return nums[0]
for i in range(0, size):
if(nums[i] < 0):
validate = i
if(size == validate + 1):
return []
#Create array 'cache' to store non-consecutive maximum values
#cache = [0]*(size + 1)
#base case
cache[0][2] = nums[0]
#temp = nums[0]
cache[0][1] = nums[0]
for i in range(1, size):
#temp1 = temp
cache[i][2] = nums[i] #I store the array numbers at index [I][2]
cache[i][1] = cache[i - 1][0] + nums[I] #the max sum is store here
cache[i][0] = max(cache[i - 1][1], cache[i -1][0]) #current sum is store there
maxset = 0
for i in range(0, size): #I get the max sum
if(cache[i][1] > maxset):
maxset = cache[i][1]
for i in range(0, size): #I get the first element here
if(cache[i][1] == maxset):
temp = cache[i][2]
count = 0
for i in range(0, size): # I check at what index in the nums array the index 'temp' is store
if(nums[i] != temp):
count += 1
if(size - 1 == count): #iterate through the nums array to apend the non-adjacent elements
if(count % 2 == 0):
for i in range(0, size):
if i % 2 == 0 and i < size:
list.append(nums[i])
else:
for i in range(0, size):
if i % 2 != 0 and i < size:
list.append(nums[i])
list[:]= [item for item in list if item >= 0]
return list
if __name__ == '__main__':
A = [7,2,-3,5,-4,8,6,3,1]
B = [7,2,5,8,6]
C = [-2,3,1,10,3,-7]
'''
Also, I came up with the idea to create another array to store the elements that added to the max sum, but I don't know how to do that.
Any guidance would be appreciated and thanks beforehand!
Probably not the best solution , but what about trying with recursion ?
tests = [([7, 2, -3, 5, -4, 8, 6, 3, 1], [7, 5, 8, 3]),
([7, 2, 5, 8, 6], [7, 5, 6]),
([-2, 3, 1, 10, 3, -7], [3, 10]),
([7, 2, 9, 10, 1], [7, 9, 1]),
([7, 2, 5, 18, 6], [7, 18]),
([7, 20, -3, -5, -4, 8, 60, 3, 1], [20, 60, 1]),
([-7, -20, -3, 5, -4, 8, 60, 3, 1], [5, 60, 1])]
def bigest(arr, cache, final=[0]):
if len(arr) == 0:
return cache
for i in range(len(arr)):
result = bigest(arr[i + 2:], [*cache, arr[i]], final)
if sum(cache) > sum(final):
final[:] = cache[:]
if sum(result) > sum(final):
final[:] = result[:]
return result
if __name__ == "__main__":
print("has started")
for test, answer in tests:
final = [0]
bigest(test, [], final)
assert final == answer, "not matching"
print(f"for {test} , answer: {final} ")
Here is a dynamic programming approach.
def best_skips (data):
answers = []
for i in range(len(data)):
x = data[i]
best = [0, None]
for prev in answers[0:i-1]:
if best[0] < prev[0]:
best = prev
max_sum, path = best
answers.append([max_sum + x, [x, path]])
answers.append([0, None]) # Add empty set as an answer.
path = max(answers)[1]
final = []
while path is not None:
final.append(path[0])
path = path[1]
return final
I have a list of list and I want to remove zero values that are between numbers in each list. All my lists inside my list have same lenght.
For example:
List1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
desired output:
list2=[[0,1,2,3,0,0],[0,5,6,9,0]]
I was thinking about using indices to identify the first non zero value and last non zero value, but then I don't know how I can remove zeros between them.
You have the right idea, I think, with finding the first and last indices of nonzeroes and removing zeroes between them. Here's a function that does that:
def remove_enclosed_zeroes(lst):
try:
first_nonzero = next(
i
for (i, e) in enumerate(lst)
if e != 0
)
last_nonzero = next(
len(lst) - i - 1
for (i, e) in enumerate(reversed(lst))
if e != 0
)
except StopIteration:
return lst[:]
return lst[:first_nonzero] + \
[e for e in lst[first_nonzero:last_nonzero] if e != 0] + \
lst[last_nonzero:]
list1 = [[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
list2 = [remove_enclosed_zeroes(sublist) for sublist in list1]
# [[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]
Inspired by #python_user I thought about this a bit more and came up with this simpler solution:
def remove_internal_zeros(lst):
return [v for i, v in enumerate(lst) if v or not any(lst[i+1:]) or not any(lst[:i])]
This works by passing any value from the original list which is either
not zero (v); or
zero and not preceded by a non-zero value (not any(lst[:i])); or
zero and not followed by a non-zero value (not any(lst[i+1:]))
It can also be written as a list comprehension:
list2 = [[v for i, v in enumerate(lst) if v or not any(lst[:i]) or not any(lst[i+1:])] for lst in list1]
Original Answer
Here's another brute force approach, this pops all the zeros off either end of the list into start and end lists, then filters the balance of the list for non-zero values:
def remove_internal_zeros(l):
start_zeros = []
# get starting zeros
v = l.pop(0)
while v == 0 and len(l) > 0:
start_zeros.append(0)
v = l.pop(0)
if len(l) == 0:
return start_zeros + [v]
l = [v] + l
# get ending zeros
end_zeros = []
v = l.pop()
while v == 0 and len(l) > 0:
end_zeros.append(0)
v = l.pop()
# filter balance of list
if len(l) == 0:
return start_zeros + [v] + end_zeros
return start_zeros + list(filter(bool, l)) + [v] + end_zeros
print(remove_internal_zeros([0,1,0,2,3,0,0]))
print(remove_internal_zeros([0,5,6,0,0,9,0]))
print(remove_internal_zeros([0,0]))
print(remove_internal_zeros([0,5,0]))
Output:
[0, 1, 2, 3, 0, 0]
[0, 5, 6, 9, 0]
[0, 0]
[0, 5, 0]
I think this has to be done with brute force.
new = []
for sub in List1:
# Find last non-zero.
for j in range(len(sub)):
if sub[-1-j]:
lastnonzero = len(sub)-j
break
print(j)
newsub = []
firstnonzero = False
for i,j in enumerate(sub):
if j:
firstnonzero = True
newsub.append(j)
elif i >= lastnonzero or not firstnonzero:
newsub.append(j)
new.append(newsub)
print(new)
Please try this, remove all 0 between numbers in each list.:
list1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
rowIndex=len(list1) # count of rows
colIndex=len(list1[0]) # count of columns
for i in range(0, rowIndex):
noZeroFirstIndex = 1
noZeroLastIndex = colIndex - 2
for j in range(1, colIndex - 1):
if(list1[i][j] != 0):
noZeroFirstIndex = j
break
for j in range(colIndex -2, 0, -1):
if(list1[i][j] != 0):
noZeroLastIndex = j
break
for j in range(noZeroLastIndex, noZeroFirstIndex, -1):
if(list1[i][j] == 0 ):
del list1[i][j]
print(list1)
Result:
[[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]
I wrote a pretty straight-forward approach. Try this.
def removeInnerZeroes(list):
listHold=[]
listNew = []
firstNonZeroFound = False
for item in list:
if item==0:
if firstNonZeroFound:
listHold.append(item)
else:
listNew.append(item)
else:
firstNonZeroFound=True
listHold.clear()
listNew.append(item)
listNew.extend(listHold)
return listNew
complexList = [[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
print(complexList)
complexListNew = []
for listi in complexList:
complexListNew.append(removeInnerZeroes(listi))
print(complexListNew)
One way to do it is to treat each sublist as 3 sections:
Zeros at the front, if any
Zeros at the end, if any
Numbers in the middle from which zeros are to be purged
itertools.takewhile is handy for the front and end bits.
from itertools import takewhile
List1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
def purge_middle_zeros(numbers):
is_zero = lambda x: x==0
leading_zeros = list(takewhile(is_zero, numbers))
n_lead = len(leading_zeros)
trailing_zeros = list(takewhile(is_zero, reversed(numbers[n_lead:])))
n_trail = len(trailing_zeros)
mid_numbers = numbers[n_lead:-n_trail] if n_trail else numbers[n_lead:]
mid_non_zeros = [x for x in mid_numbers if x]
return leading_zeros + mid_non_zeros + trailing_zeros
list2 = [purge_middle_zeros(sub_list) for sub_list in List1]
list2
[[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]
Other notes:
the lambda function is_zero tells takewhile what the criteria are for continuing, in this case "keep taking while it's a zero"
for the mid_non_zeros section the list comprehension [x for.... ] takes all the numbers except for the zeros (the if x at the end applies the filter)
slicing notation to pick out the middle of the list, numbers[from_start:-from_end] with the negative -from_end meaning 'except for this many elements at the end'. The case where there are no trailing zeros requires a different slice expression, i.e. numbers[from_start:]
I wrote the following code out of curiosity, that is somewhat intuitive and mimics a "looking item-by-item" approach.
def remove_zeros_inbetween(list_):
new_list = list_.copy()
for j, l in enumerate(list_): # loop through the inner lists
checking = False
start = end = None
i = 0
deleted = 0
while i < len(l): # loop through the values of an inner list
if l[i] == 0: # ignore
i += 1
continue
if l[i] != 0 and not checking: # non-zero value found
checking = True # start checking for zeros
start = i
elif l[i] != 0 and checking: # if got here and checking, the finish checking
checking = False
end = i
if start and end: # if both values have been set, i.e, different to None
# delete values in-between
new_list[j] = new_list[j][:(start+1-deleted)] + new_list[j][(end-deleted):]
deleted += end - start - 1
if l[i] != 0: # for the case of two non-zero values
start = i
checking = True
else:
i = end # ignore everything up to end
end = None # restart check
i += 1
return new_list
>>> remove_zeros_inbetween([[0, 1, 0, 2, 3, 0, 5], [0, 5, 6, 0, 0, 9, 4]])
[[0, 1, 2, 3, 5], [0, 5, 6, 9, 4]]
>>> remove_zeros_inbetween([[0, 0], [0, 3, 0], [0]]))
[[0, 0], [0, 3, 0], [0]]
>>> remove_zeros_inbetween([[0, 0, 0, 0]]))
[[0, 0, 0, 0]]
You start by replacing 0 by "0" - which is not necessary. Secondly your filter call does not save the resulting list; try:
list1[i] = list(filter(lambda a: a !=0, list1[1:-1])) # changed indexing , I suppose this could work
This question already has answers here:
Sorting by absolute value without changing to absolute value
(3 answers)
Closed 3 years ago.
Given an array of ints:
(-20, -5, 10, 15)
the program should output:
[-5, 10, 15, -20]
I first tried to think about the pseudocode:
for each element in the array
if its absolute value is higher than the nxt element
swap them
And I implemented this as:
def sort_by_abs(numbers_array: tuple) -> list:
numbers_array = list(numbers_array)
for i, number in enumerate(numbers_array):
if i == len(numbers_array) - 1:
break
elif abs(number) > abs(numbers_array[i+1]):
temp = number
numbers_array[i] = numbers_array[i+1]
numbers_array[i+1] = temp
return numbers_array
However it fails when we have the following sequence:
(1, 2, 3, 0)
it outputs
[1, 2, 0, 3]
And I understand that when the element which needs to be moved is not at first there could be other numbers to be moved to the left
Then I tried sorting the list first, and it solves this case:
def sort_by_abs(numbers_array: tuple) -> list:
numbers_array = sorted(list(numbers_array))
for i, number in enumerate(numbers_array):
if i == len(numbers_array) - 1:
break
elif abs(number) > abs(numbers_array[i+1]):
temp = number
numbers_array[i] = numbers_array[i+1]
numbers_array[i+1] = temp
return numbers_array
However when we have: (-1, -2, -3, 0) it outputs [-2, -1, 0, -3] and it should return [0, -1, -2, -3]
How could be it improved?
Just use python builtin sorted with key abs which sorts considering the absolute value of the integer
def sort_by_abs(numbers_array):
return sorted(tuple(numbers_array), key=abs)
print(sort_by_abs((-5, 10, 15, -20)))
print(sort_by_abs((-1, -2, -3, 0)))
print(sort_by_abs((1, 2, 3, 0)))
The output will be
[-5, 10, 15, -20]
[0, -1, -2, -3]
[0, 1, 2, 3]
I'm trying an exercise that wants me to return a new list that contains all the same elements except the negative numbers which are turned into zeros in the returned list.
I have used a for loop to loop through the parameter list and if the number is below 0, I would append it to a new list but times it by 0. However, I get weird outputs such as empty lists. For example, the code below should print:
[0, 0, 9, 0, 0, 34, 1]
[9, 34, 1]
[0, 0, 0]
Please stick to using list methods thanks.
The code:
def get_new_list_no_negs(num_list):
new_list = []
for i in range(len(num_list)):
if i < 0:
new_list.append(num_list[i] * 0)
return new_list
def main():
print("1.", get_new_list_no_negs([-3, -6, 9, 0, 0, 34, 1]))
print("2.", get_new_list_no_negs([9, 34, 1]))
print("3.", get_new_list_no_negs([-9, -34, -1]))
main()
This should do:
def get_new_list_no_negs(num_list):
return [max(num, 0) for num in num_list]
the max function is a python builtin that will return the largest between the passed numbers.
Try this
l = [-2, -1, 0, 1, 2]
# this
l = [i for i in l if i > 0 else 0]
# or
l = [max(i, 0) for i in l]
The enumerate() function adds a counter to an iterable.
So for each element in a cursor, a tuple is produced with (counter, element); the for loop binds that to row_number and row, respectively.
l = [-2, -1, 0, 1, 2]
for index, value in enumerate(l):
if value < 0:
l[index] = 0
print(l)
O/P:
[0, 0, 0, 1, 2]
I have a list such as
[0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
I want to find start and end position where element are positive:
[[3,5],[9,12],[16,18]]
what is the best way to do this in python?
(build in function in python such as: find,lambda,itemgetter and so on.)
And lastly, regex version. ;)
input = [0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
input3 = str(list(
map(lambda i_x: i_x[0] * (i_x[1] and (1, -1)[i_x[1] < 0]), enumerate(input))
))
import re
s = re.sub(r'([\[ ]0[\],])+', ' ', input3)
s = s.replace(', ', '], [')
if s[-1:] != ']':
s = s[:-2] + ']'
s = '[' + s[2:]
s = re.sub(r' [0-9]+,', '', s)
output = list(eval(s))
print(output) # [[3, 5], [9, 12], [16, 18]]
Crude for solution. :(
input = [0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
output = []
pair = []
for i in range(len(input)):
if input[i] > 0:
if len(pair) > 1:
pair.pop()
pair.append(i)
else:
if pair:
output.append(pair)
pair = []
print(output) # [[3, 5], [9, 12], [16, 18]]
Not sure if ranges can go off the ends of the array or not.
def get_positive_ranges(a):
in_range = False
result = []
for i in range(len(a)):
if not in_range:
if a[i] > 0:
in_range = True
first = i
else: # Inside a range
if a[i] <= 0: # End of range
in_range = False
result.append([first, i - 1])
if in_range: # Tidy
result.append([first, i])
return result
print(get_positive_ranges([0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]))
print(get_positive_ranges([]))
print(get_positive_ranges([1]))
print(get_positive_ranges([0, 1]))
print(get_positive_ranges([0, 1, 0]))
Here is a numpy solution, not sure if this is better than a naive for-loop though; see inline comments for explanation.
import numpy as np
a = np.array([0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0])
# get indices of non-zero elements in a
nze = a.nonzero()[0]
# check where the differences of these indices are unequal to one; there you have a jump to/from 0
nze_diff = np.where(np.diff(nze) > 1)[0] + 1
# if a starts with 0, add the index 0
if nze_diff[0] != 0:
nze_diff = np.insert(nze_diff, 0, 0)
# store output
res = []
# loop through the indices and add the desired slices
for ix, i in enumerate(nze_diff):
try:
sl = nze[i:nze_diff[ix + 1]]
res.append([sl[0], sl[-1]])
# means we reached the end of nze_diff
except IndexError:
sl = nze[i:]
res.append([sl[0], sl[-1]])
If you run it for your a, you receive the desired output:
[[3, 5], [9, 12], [16, 18]]
There are probably smarter solutions than this, but this might get you started.
If you want to get the entire range, it simplifies a bit:
res2 = []
for ix, i in enumerate(nze_diff):
try:
res2.append(nze[i:nze_diff[ix + 1]])
except IndexError:
res2.append(nze[i:])
Then res2 would be:
[array([3, 4, 5]), array([ 9, 10, 11, 12]), array([16, 17, 18])]
This works
lst = [0, 0, 0, 12, 34, 86, 0, 0, 0, 95, 20, 1, 6, 0, 0, 0, 11, 24, 67, 0, 0, 0]
n = len(lst)
starting_points = [i for i in range(n) if lst[i] > 0 and (lst[i - 1] == 0 or i == 0)]
end_points = [next((i for i in range(j + 1, n) if lst[i] == 0), n) - 1 for j in starting_points]
print zip(starting_points, end_points)
output
[(3, 5), (9, 12), (16, 18)]
If performance is the key, you should test which implementaion is the most fast with your very long list. Anyway, this is 'no array acess by index' version, hopefully for boosting speed. And it uses map, lambda, index(find), if it pleases you. Though, of course it uses while.
input = [0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
output = []
input2 = list(map(lambda x: x and (1, -1)[x < 0], input)) # mapping by 'math.sign'-like func
start = end = 0
while end < len(input2):
try:
start = input2.index(1, end + 1)
end = input2.index(0, start) - 1
output.append([start, end])
except ValueError:
break
if start >= end:
output.append([start, len(input2) - 1])
print(output) # [[3, 5], [9, 12], [16, 18]]