I am trying to find an algorithm to solve the following problem with python.
Given a list of integers, check if after dropping two elements of the list, is it possible to divide the list in 3 where the sum of the consecutive numbers are equals.
For example:
A = [1, 3, 4, 2, 2, 2, 1, 1, 2] should return TRUE because we would drop the elements in bold the list can be slit in [1, 3], [2, 2], [2, 1, 1] where all sum 4
B = [1, 1, 1, 1, 1, 1] should return FALSE
C = [1, 1, 3, 1, 1, 1034, 5, 9900, 1, 2] should also return FALSE because eventhough after droping the numbers in bold you can sum have the numbers to sum 5 (1,1,3), (5), (1, 1, 1, 2) the list should be sorted first and that's not allowed.
I have come with a solution that seems to work but it is very, very bad and not sure if always work, and the complexity is too high when should be O(n)
I don't know how to iterate removing 2 numbers from a list without having a complexity of O(n^2)
Thanks
It is indeed possible to solve this problem in O(n) (given that the integers are positive) where n is the size of your array A.
It can be surprising since the problem reminds of the bin packing, or subset sum problems, but the fact that we look for consecutive sum in the array makes the task much easier.
Here is a python code doing it:
def find_couple_list_sum(int_list):
n = len(int_list)
left_sum = [0 for _ in range(n)]
for i in range(1, n):
left_sum[i] = left_sum[i-1] + int_list[i-1]
right_sum = [0 for _ in range(n)]
for j in range(n-2, -1, -1):
right_sum[j] = right_sum[j+1] + int_list[j+1]
total_sum = sum(int_list)
print(left_sum)
print(right_sum)
print(total_sum)
i, j = 0, n-1
while True:
print(i, j)
mid_sum = total_sum - left_sum[i] - right_sum[j] - int_list[i] - int_list[j]
if left_sum[i] == right_sum[j] and left_sum[i] == mid_sum:
return i, j
elif i == j:
return None, None
elif left_sum[i] < right_sum[j]:
i += 1
else:
j -= 1
int_list = [1, 3, 4, 2, 2, 2, 1, 1, 2]
print(find_couple_list_sum(int_list))
The whole thing runs indeed in O(n).
this is perhaps a very simple question
I have a list that looks like this:
a=[0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
I am struggling to find a simple python code that replaces when n or less consecutive 1s are found to 0s and creates a new list with the new values
So if
n = 2
b = [0,0,0,2,3,2,0,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,0,0,3,4,0]
if
n = 3
b = [0,0,0,2,3,2,0,2,0,3,4,1,1,1,1,0,0,0,0,4,5,0,0,0,3,2,0,2,0,0,3,4,0]
I have highlighted the new replaces values in each example
You can try this:
import itertools
a=[0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
n = 3
new_list = list(itertools.chain(*[[0]*len(b) if a == 1 and len(b) <= n else b for a, b in [(c, list(d)) for c, d in itertools.groupby(a)]]))
Output:
[0, 0, 0, 2, 3, 2, 0, 2, 0, 3, 4, 1, 1, 1, 1, 0, 0, 0, 0, 4, 5, 0, 0, 0, 3, 2, 0, 2, 0, 0, 3, 4, 0]
"One"-liner, using some itertools:
from itertools import groupby, chain
a=[0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
list(
chain.from_iterable(
([0] * len(lst) if x == 1 and len(lst) <= n else lst
for x, lst in ((k, list(g)) for k, g in groupby(a)))
)
)
# [0,0,0,2,3,2,0,2,0,3,4,1,1,1,1,0,0,0, 0,4,5,1,1,1,3,2,0,2,0,0,3,4,0]
groupby groups the initial list into groups of identical objects. Its output is an iterator of pairs (k, g) where k is the element that is the grouping key and g is an iterator producing the actual elements in the group.
Since you cannot call len on an iterator, this listifies the groups and chains the resulting lists except lists of 1 of the appropriate lengthes. Those are replaced by lists of 0 of the same length.
In single steps (using intermediate lists instead of generators):
grouped_lists_by_key = [k, list(g)) for k, g in groupby(a)]
# [(0, [0]), (1, [1, 1]), ...]
grouped_lists = [[0] * len(lst) if x == 1 and len(lst) <= n else lst for x, lst in grouped]
# [[0], [0, 0], [2], [3], ...]
flattened = chain.from_iterable(grouped_lists)
# [0, 0, 0, 2, 3, ...]
Non-oneliner using itertools.groupby():
a = [0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
n = 2
b = []
for k, g in groupby(a):
l = list(g)
if k == 1 and len(l) <= n:
b.extend([0]*len(l))
else:
b.extend(l)
print(b)
Try this:
def replacer(array, n):
i, consec = 0, 0
while i < len(array):
if array[i] == 1:
consec += 1
else:
if consec >= n:
for x in range(i-consec, i):
array[x] = 0
consec = 0
i += 1
return array
Longer than others, but arguably straightforward:
a = [0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
def suppress_consecutive_generator(consecutive=2, toreplace=1, replacement=0):
def gen(l):
length = len(l)
i = 0
while i < length:
if l[i] != toreplace:
yield l[i]
i += 1
continue
j = i
count = 0
while j < length:
if l[j] != toreplace:
break
count += 1
j += 1
i += count
if count <= consecutive:
for _ in range(count):
yield replacement
else:
for _ in range(count):
yield toreplace
return gen
print(list(suppress_consecutive_generator()(a)))
This question already has answers here:
Python: A program to find the LENGTH of the longest run in a given list?
(6 answers)
Closed 5 years ago.
how do I find out the maximum amount of 1s (or any element I would like) next to each other in a list?
l = [2, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 2, 7, 1, 1, 1]
In this case I would need a function that will return a 4.
Thank you.
The groupby() function can be used for this:
import itertools
l = [2, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 2, 7, 1, 1, 1]
print(max([len(list(g))*k for k, g in itertools.groupby(l, lambda x: x == 1)]))
Manually:
def makeMyHomework(li):
'''reads a list of int's and looks for the logest run of one int'''
curVal = None
curCount = 0
maxVal = None
maxCount = -1
for n in l:
if curVal == None:
curVal = n
if curVal == n:
curCount +=1
if curVal != n:
if curCount > maxCount: # check if current run > max so far
maxVal = curVal # store new max value
maxCount = curCount # store new max count
curVal = n # init the act ones
curCount = 1 # -"-
# print (n, ":", curVal, curCount, maxVal,maxCount)
# edge case - the max list is the last run
if curCount > maxCount:
maxVal = curVal
maxCount = curCount
return (maxVal, maxCount) # tuple of (value, number of occurences)
l = [2, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 2, 7, 1, 1, 1,2,2,2,2,2]
print(makeMyHomework(l))
This question already has an answer here:
Dynamic programming for primitive calculator
(1 answer)
Closed 6 years ago.
This assignment aims to implement a dynamic programming approach to a primitive calculator that can only add 1, multiply by 2 and multiply by 3. So with an input of n determine the minimum number of operations to reach n. I've implemented a very naive dp or what I think is a dp approach. It is not working. I have no-one else to ask. For an input of n = 5 the output of the below is: ([0, 1, 2, 2, 3, 4], [1, 1, 2, 3, 4, 5]) whereas there are two correct outputs for the list numbers = [1, 2, 4, 5] or [1, 3, 4, 5]. Some help would be greatly appreciated.
def DPmin_operations(n):
numbers = []
minNumOperations = [0]*(n+1)
numOps = 0
numbers.append(1)
for k in range(1,n+1):
minNumOperations[k] = 10000
# for *3 operator
if k % 3 == 0:
numOps = minNumOperations[k//3] + 1
if numOps < minNumOperations[k]:
minNumOperations[k] = numOps
numbers.append(k)
# for *2 operator
elif k % 2 == 0:
numOps = minNumOperations[k//2] + 1
if numOps < minNumOperations[k]:
minNumOperations[k] = numOps
numbers.append(k)
# for + 1 operator
elif k >= 1:
numOps = minNumOperations[k - 1] + 1
if numOps < minNumOperations[k]:
minNumOperations[k] = numOps
numbers.append(k)
return (minNumOperations, numbers)
Note that the elif blocks should really be if blocks. Currently, you're using a greedy algorithm of always trying to divide by 3; if that fails, then trying to divide by 2; if that fails, then subtracting 1. It's possible that a number is divisible by 6 so that all three options are possible, and yet dividing by 2 is more optimal then dividing by 3.
As for getting your list of numbers, do that at the end. Store all possible parents, then work backwards from your goal to see how you got there.
def dp_min_ops(n):
all_parents = [None] * (n + 1)
all_min_ops = [0] + [None] * n
for k in range(1, n + 1):
curr_parent = k - 1
curr_min_ops = all_min_ops[curr_parent] + 1
if k % 3 == 0:
parent = k // 3
num_ops = all_min_ops[parent] + 1
if num_ops < curr_min_ops:
curr_parent, curr_min_ops = parent, num_ops
if k % 2 == 0:
parent = k // 2
num_ops = all_min_ops[parent] + 1
if num_ops < curr_min_ops:
curr_parent, curr_min_ops = parent, num_ops
all_parents[k], all_min_ops[k] = curr_parent, curr_min_ops
numbers = []
k = n
while k > 0:
numbers.append(k)
k = all_parents[k]
numbers.reverse()
return all_min_ops, numbers
print(dp_min_ops(5)) # ([0, 1, 2, 2, 3, 4], [1, 3, 4, 5])
print(dp_min_ops(10)) # ([0, 1, 2, 2, 3, 4, 3, 4, 4, 3, 4], [1, 3, 9, 10])