Group list by sum of items [closed] - python

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I'm looking for an effective way to group a list of integers into a list of lists, where the sum of the original items doesn't exceed a given number.
Please consider this list of integers:
[1, 1, 1, 1, 2, 2, 1, 3, 1, 1, 2]
Which should be grouped so that the sum of the items never exceeds 3:
[[1, 1, 1], [1, 2], [2, 1], [3], [1, 1], [2]]

def group(lst, limit):
lim = 0
grp = []
for x in lst:
if x + lim > limit:
yield grp
grp = []
lim = 0
grp.append(x)
lim += x
yield grp
print list(group([1, 1, 1, 1, 2, 2, 1, 3, 1, 1, 2], 3))

Using itertools.groupby:
import itertools
def sumkey(n):
sc = [0, 0] # sum, count => group by
def keyfunc(x):
sc[0] += x
if sc[0] > n:
sc[1] += 1
sc[0] = x
return sc[1]
return keyfunc
xs = [1, 1, 1, 1, 2, 2, 1, 3, 1, 1, 2]
print([list(grp) for _, grp in itertools.groupby(xs, key=sumkey(3))])
In Python 3, sumkey could be written as following using nonlocal:
def sumkey(n):
sum_, count = 0, 0
def keyfunc(x):
nonlocal sum_, count
sum_ += x
if sum_ > n:
count += 1
sum_ = x
return count
return keyfunc

Not the most clever solution, but clean and simple enough:
def makeList(inList, n):
aList = []
while len(inList) > 0 and sum(aList) + inList[0] <= n :
aList.append(inList.pop(0))
return aList
def groupLists(inList, n, outList = []):
if not n:
raise ValueError("Please enter a positive integer")
while len(inList):
outList.append(makeList(inList, n))
return outList
print groupLists([1,1,1,1,2,2,1,3,1,1,2], 3)

Related

Algorithm Lists with python

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).

Replace when n or less consecutive values are found

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)))

Python 3: find max elements equal to each other in list [duplicate]

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))

Dynamic Programming - Primitive Calculator Python [duplicate]

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])

Python: issue with list elements comparision

The task is to define a function with argument x, where x is a list of integers.The function should print out the median value of items in it. My function always return None, I've tried to fix it, but after many tries I am out of ideas.
Code:
def median(x):
x = sorted(x)
med_val = 0
if len(x) % 2.0 == 0:
for i in x:
if len(x[:i]) == len(x[i:]) + 1:
return x[i]
else:
for i in x:
if len(x[:i + 1]) == len(x[i: + 1]) + 1:
return (x[i] + x[i + 1]) / 2
print (median([1, 4, 2, 6, 8]))
Any ideas how to make it work?
In your method, replace the print statements by return expression. And in your function call, do:
print(median(list))
PS: It is better not to use list as a variable name in python.
Here's a slightly different approach:
def median(x):
x = sorted(x)
middle_index = float(len(x)/2.0) # this 2.0 part is very important
if middle_index % 2 == 0: #check if it's even
med_val = float((x[int(middle_index)]+x[int(middle_index-1)])/2) # take the average of the 2 middle values if so
else: med_val = x[int(middle_index)] #otherwise, select the center value
return med_val #return median
lists = [[2, 1, 4, 6], [9,1,1,9,3], [1, 4, 2, 6, 8]]
for item in lists:
print median(item)
In: [2, 1, 4, 6]
Out: 3.0
In: [9, 1, 1, 9, 3]
Out: 3
In: [1, 4, 2, 6, 8]
Out: 4

Categories