Create unique combinations regardless of subset size - python

I am doing a project that requires getting unique combinations in Python regardless of the subset size.
Lets say I have a list of sizes [1,2,2,3,4,5] and a size bound of 8. I want combinations that have all the elements and no repeat such that the sum of each combination should be less than or equal to 8. Another restriction is that the subtraction of the sum and the bound should be minimum.
For example in this case the answer should be [5,3] [4,2,2] [3,1] this way the total waste out of 8 will be 4 which is (3+1)-8=4.

You could use a recursive function to "brute force" the packing combinations and get the best fit out of those:
def pack(sizes,bound,subset=[]):
if not sizes: # all sizes used
yield [subset] # return current subset
return
if sizes and not subset: # start new subset
i,m = max(enumerate(sizes),key=lambda s:s[1])
subset = [m] # using largest size
sizes = sizes[:i]+sizes[i+1:] # (to avoid repeats)
used = sum(subset)
for i,size in enumerate(sizes): # add to current subset
if subset and size>subset[-1]: # non-increasing order
continue # (to avoid repeats)
if used + size <= bound:
yield from pack(sizes[:i]+sizes[i+1:],bound,subset+[size])
if sizes:
for p in pack(sizes,bound): # add more subsets
yield [subset,*p]
def bestFit(sizes,bound):
packs = pack(sizes,bound)
return min(packs,key = lambda p : bound*len(p)-sum(sizes))
output:
for p in pack([1,2,3,4,5],8):
print(p,8*len(p)-sum(map(sum,p)))
[[5, 1], [4], [3, 2]] 9
[[5, 2, 1], [4, 3]] 1
[[5, 2], [4, 3, 1]] 1
[[5, 2], [4], [3, 1]] 9
[[5, 3], [4, 2, 1]] 1
[[5, 3], [4], [2, 1]] 9
[[5], [4, 1], [3, 2]] 9
[[5], [4, 2], [3, 1]] 9
[[5], [4, 3], [2, 1]] 9
[[5], [4], [3, 2, 1]] 9
[[5], [4], [3], [2, 1]] 17
print(*bestFit([1,2,3,4,5],8))
# [5, 2, 1] [4, 3]
print(*bestFit([1,2,3,4,5,6,7,8,9],18))
# [9, 1] [8, 4, 3, 2] [7, 6, 5]
This will take exponentially longer as your list of sizes gets larger but it may be enough if you only have very small inputs

You probably need something like itertools.combinations, that will give you all the possible combinations of elements in sublists of given lenght without duplicate elements.
If you want to know more about function combinations, i would suggest to read also this.
Something like this should work:
for i in range(8//min(myList)):
for j in itertools.permutations(myList, i):
if sum(j) == 8:
print(j)
This way you are getting all the combinations of myList, and printing those ones of which element's sum is 8.
A function like this may be useful:
def permutationsWithSum(myList: list[int], n: int):
for i in range(n//min(myList)):
for j in itertools.permutations(myList, i):
if sum(j) == n:
yield j

Related

I want to get only those subset which have k elements in it

L=[1,2,3,4,5,6,7]
K=2
The ans i want
L=[[1,2],[2,3],[3,4][4,5],[5,6],[6,7]]
In ans there should be no subset which doesn't have k elements in it
You can iterate a number starting from 0 to the maximum number from which at least K elements exists, then create the list of list out the items starting and ending with required indices, you can use List Comprehension to accomplish this:
>>> [L[i:i+K] for i in range(len(L)-K+1)]
[[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]]
You can use list comprehension
answer = [[L[j] for j in range(i, i + K)] for i in range(len(L) - K)]

Generate a unique list of pairs from a set of values that does not match previous n pairings

I'm working in python for this problem.
Given a list of values that can increase or decrease in size between iterations (it will be padded to always be even)
users = [1,2,3,4,5,6,7,8]
Create a new random set of pairings for the users (order does not matter)
pairs_2 = [[2,5],[4,6],[3,8],[1,7]]
and ensure that the pairing does not overlap with the previous n sets of pairs
pairs_1 = [[7,10],[3,5],[1,6],[2,8],[4,9]]
pairs_0 = [[2,4],[3,6],[1,5]]
My current implementation just involves generating a random set of pairs and then doing a set intersection against the previous n pairings to see if it is unique or not, this obviously does not scale well nor guarantee a solution in any reasonable time.
I've also tried to instead generate all possible combinations of pairs, and then find the difference between that and all previous pairings, but then there is the problem of selecting exactly users/2 pairs from the list that contains all users. It feels like the first half of this solution is good as it guarantees only new pairs, but then requires some additional logic to select the pairs.
import random
myList = [[1,2,3,4,5,6],[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8]]
oldList = []
for e in range(0,len(myList)):
oldList.append([])
for d in range(0,len(myList[e]),2):
while True:
f = False
a = random.choice(myList[e])
myList[e].remove(a)
#print(myList,e)
b = random.choice(myList[e])
myList[e].remove(b)
#print(myList,e)
for c in oldList:
if [a,b] in c:
myList[e].append(a)
myList[e].append(b)
#print("Runned")
f = True
if f:
f = False
continue
oldList[e].append([a,b])
break
print(oldList)
All the result will be in oldList , It depend on which one you put in first
On your example , you should put in
myList = [[1,2,3,4,5,6],[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8]]
And the result will be:
[[[6, 3], [5, 4], [2, 1]], [[5, 2], [10, 8], [3, 4], [1, 9], [7, 6]], [[6, 1], [8, 5], [7, 3], [4, 2]]]
And to get different pair out according to your example
pair_0 = oldList[0]
pair_1 = oldList[1]
pair_2 = oldList[2]
and the result will be
pair_0 = [[6, 3], [5, 4], [2, 1]]
pair_1 = [[5, 2], [10, 8], [3, 4], [1, 9], [7, 6]]
pair_2 = [[6, 1], [8, 5], [7, 3], [4, 2]]

Python deduce best number among list of lists [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
A= [[], [2, 3], [1], [1], [3]]
I have a list of lists. What I want to do is determine a best number (represent a choice) among the lists. --- a general algorithm to do so
The rules:
1) All the lists are ordered descendingly (left to right), so we always choose the number in the earlier sub-list (in this case [2, 3])
2) If there are multiple numbers(can't decided), we keep going down, until the number appears in the following earliest sub-list. In the case of A, both [1] does not contain 2 or 3 and as the last item [3] contains 3, we decide the best number in A is 3.
I all make more examples to be more clear.
B=[[5], [0, 8], [0, 8], [0, 8], [1]]
The best number is 5.
C=[[0, 1], [0, 3], [0], [0], [2]]
The best number is 0.
D=[[], [3, 6], [3, 5, 6], [6], [1]]
The best number is 6.
Anyone has any idea how to write the algorithm... got stuck.
Thanks.
You can do it in three steps:
Iterate over the nested list and extract all numbers from single-element lists like [5]
Flatten the nested list into a list of numbers
Iterate over the flattened list until you find a valid number
def find_best(choices):
# make a set of valid output numbers
valid_numbers = {sublist[0] for sublist in choices if len(sublist) == 1}
# flatten the nested input list
flat_list = (number for sublist in choices for number in sublist)
# find the first number that's a valid output
return next(number for number in flat_list if number in valid_numbers)
print(find_best([[], [2, 3], [1], [1], [3]])) # 3
print(find_best([[5], [0, 8], [0, 8], [0, 8], [1]])) # 5
print(find_best([[0, 1], [0, 3], [0], [0], [2]])) # 0
print(find_best([[], [3, 6], [3, 5, 6], [6], [1]])) # 6
Here is a function that works fine for all cases and return the list of all first candidates encountered if no choice can be made to separate them.
def find_best(list_of_lists):
i = 0
while len(list_of_lists[i]) == 0:
i+=1
list_containing_candidates = list_of_lists[i][:]
if len(list_containing_candidates) == 1 :
return list_containing_candidates[0]
else:
if i+1 < len(list_of_lists):
for next_list in list_of_lists[i+1:]:
for candidate in list_containing_candidates[:]:
if candidate not in next_list:
list_containing_candidates.remove(candidate)
if len(list_containing_candidates) == 0:
list_containing_candidates = list_of_lists[i][:]
elif len(list_containing_candidates) == 1:
return list_containing_candidates[0]
return list_of_lists[i] # ambigous case, entire list of candidates returned
print(find_best([[], [2, 3], [1], [1], [3]])) # 3
print(find_best([[5], [0, 8], [0, 8], [0, 8], [1]])) # 5
print(find_best([[0, 1], [0, 3], [0], [0], [2]])) # 0
print(find_best([[], [3, 6], [3, 5, 6], [6], [1]])) # 6
print(find_best([[], [3, 6], [3, 5], [6], [1]])) # 3
print(find_best([[1,3 ], [1, 3], [1,2,3], [1,3], []])) # [1,3]

Checking for duplicates in list of list and sorting them

I have a table containing:
table = [[5, 7],[4, 3],[3, 3],[2, 3],[1, 3]]
and the first values represented in each list, (5,4,3,2,1) can be said to be an ID of a person. the second values represented (7,3,3,3,3) would be a score. What I'm trying to do is to detect duplicates values in the second column which is in this case is the 3s in the list. Because the 4 lists has 3 as the second value, i now want to sort them based on the first value.
In the table, notice that [1,3] has one as the first value hence, it should replace [4,3] position in the table. [2,3] should replace [3,3] in return.
Expected output: [[5,7],[1,3],[2,3],[3,3],[4,3]]
I attempted:
def checkDuplicate(arr):
i = 0
while (i<len(arr)-1):
if arr[i][1] == arr[i+1][1] and arr[i][0] > arr[i+1][0]:
arr[i],arr[i+1] = arr[i+1],arr[i]
i+=1
return arr
checkDuplicate(table)
The code doesn't fulfil the output i wanted and i would appreciate some help on this matter.
You can use sorted with a key.
table = [[5, 7], [4, 3], [3, 3], [2, 3], [1, 3]]
# Sorts by second index in decreasing order and then by first index in increasing order
sorted_table = sorted(table, key=lambda x: (-x[1], x[0]))
# sorted_table: [[5, 7], [1, 3], [2, 3], [3, 3], [4, 3]]
You should sort the entire list by the second column, using the first to break ties. This has the advantage of correctly grouping the threes even when the seven is interpersed among them, e.g. something like
table = [[4, 3],[3, 3],[5, 7],[2, 3],[1, 3]]
In Python, you can do it with a one-liner:
result = sorted(table, key=lambda x: (-x[1], x[0]))
If you want an in-place sort, do
table.sort(key=lambda x: (-x[1], x[0]))
Another neat thing you can do in this situation is to rely on the stability of Python's sorting algorithm. The docs actually suggest doing multiple sorts in complex cases like this, in the reverse order of the keys. Using the functions from operator supposedly speeds up the code as well:
from opetator import itemgetter
result = sorted(table, key=itemgetter(0))
result.sort(key=itemgetter(1), reversed=True)
The first sort will arrange the IDs in the correct order. The second will sort by score, in descending order, leaving the IDs undisturbed for identical scores since the sort is stable.
If you want to leave the list items with non-duplicate second elements untouched, and the ability to deal with the cases where multiple second items can be duplicate, I think you'll need more than the built-in sort.
What my function achieves:
Say your list is: table = [[5, 7], [6, 1], [8, 9], [3, 1], [4, 3], [3, 3], [2, 3], [1, 3]]
It will not touch the items [5, 7] and [8, 9], but will sort the remaining items by swapping them based on their second elements. The result will be:
[[5, 7], [3, 1], [8, 9], [6, 1], [1, 3], [2, 3], [3, 3], [4, 3]]
Here is the code:
def secondItemSort(table):
# First get your second values
secondVals = [e[1] for e in table]
# The second values that are duplicate
dups = [k for k,v in Counter(secondVals).items() if v>1]
# The indices of those duplicate second values
indices = dict()
for d in dups:
for i, e in enumerate(table):
if e[1]==d:
indices.setdefault(d, []).append(i)
# Now do the sort by swapping the items intelligently
for dupVal, indexList in indices.items():
sortedItems = sorted([table[i] for i in indexList])
c = 0
for i in range(len(table)):
if table[i][1] == dupVal:
table[i] = sortedItems[c]
c += 1
# And return the intelligently sorted list
return table
Test
Let's test on a little bit more complicated table:
table = [[5, 7], [6, 1], [8, 9], [3, 1], [4, 3], [3, 9], [3, 3], [2, 2], [2, 3], [1, 3]]
Items that should stay in their places: [5, 7] and [2, 2].
Items that should be swapped:
[6, 1] and [3, 1].
[8, 9] and [3, 9]
[4, 3], [3, 3], [2, 3], [1, 3]
Drumroll...
In [127]: secondItemSort(table)
Out[127]:
[[5, 7],
[3, 1],
[3, 9],
[6, 1],
[1, 3],
[8, 9],
[2, 3],
[2, 2],
[3, 3],
[4, 3]]

Partition N items into K bins in Python lazily

Give an algorithm (or straight Python code) that yields all partitions of a collection of N items into K bins such that each bin has at least one item. I need this in both the case where order matters and where order does not matter.
Example where order matters
>>> list(partition_n_in_k_bins_ordered((1,2,3,4), 2))
[([1], [2,3,4]), ([1,2], [3,4]), ([1,2,3], [4])]
>>> list(partition_n_in_k_bins_ordered((1,2,3,4), 3))
[([1], [2], [3,4]), ([1], [2,3], [4]), ([1,2], [3], [4])]
>>> list(partition_n_in_k_bins_ordered((1,2,3,4), 4))
[([1], [2], [3], [4])]
Example where order does not matter
>>> list(partition_n_in_k_bins_unordered({1,2,3,4}, 2))
[{{1}, {2,3,4}}, {{2}, {1,3,4}}, {{3}, {1,2,4}}, {{4}, {1,2,3}},
{{1,2}, {3,4}}, {{1,3}, {2,4}}, {{1,4}, {2,3}}]
These functions should produce lazy iterators/generators, not lists. Ideally they would use primitives found in itertools. I suspect that there is a clever solution that is eluding me.
While I've asked for this in Python I'm also willing to translate a clear algorithm.
you need a recursive function to solve this kind of problem: you take the list, take a subportion of it of increasing length and apply the same procedure to the remaining tail of the list in n-1 pieces.
here is my take to the ordered combination
def partition(lista,bins):
if len(lista)==1 or bins==1:
yield [lista]
elif len(lista)>1 and bins>1:
for i in range(1,len(lista)):
for part in partition(lista[i:],bins-1):
if len([lista[:i]]+part)==bins:
yield [lista[:i]]+part
for i in partition(range(1,5),1):
print i
#[[1, 2, 3, 4]]
for i in partition(range(1,5),2):
print i
#[[1], [2, 3, 4]]
#[[1, 2], [3, 4]]
#[[1, 2, 3], [4]]
for i in partition(range(1,5),3):
print i
#[[1], [2], [3, 4]]
#[[1], [2, 3], [4]]
#[[1, 2], [3], [4]]
for i in partition(range(1,5),4):
print i
#[[1], [2], [3], [4]]
Enrico's algorithm, Knuth's, and only my glue are needed to paste together something that returns the list of lists or set of sets (returned as lists of lists in case elements are not hashable).
def kbin(l, k, ordered=True):
"""
Return sequence ``l`` partitioned into ``k`` bins.
Examples
========
The default is to give the items in the same order, but grouped
into k partitions:
>>> for p in kbin(range(5), 2):
... print p
...
[[0], [1, 2, 3, 4]]
[[0, 1], [2, 3, 4]]
[[0, 1, 2], [3, 4]]
[[0, 1, 2, 3], [4]]
Setting ``ordered`` to None means that the order of the elements in
the bins is irrelevant and the order of the bins is irrelevant. Though
they are returned in a canonical order as lists of lists, all lists
can be thought of as sets.
>>> for p in kbin(range(3), 2, ordered=None):
... print p
...
[[0, 1], [2]]
[[0], [1, 2]]
[[0, 2], [1]]
"""
from sympy.utilities.iterables import (
permutations, multiset_partitions, partitions)
def partition(lista, bins):
# EnricoGiampieri's partition generator from
# http://stackoverflow.com/questions/13131491/
# partition-n-items-into-k-bins-in-python-lazily
if len(lista) == 1 or bins == 1:
yield [lista]
elif len(lista) > 1 and bins > 1:
for i in range(1, len(lista)):
for part in partition(lista[i:], bins - 1):
if len([lista[:i]] + part) == bins:
yield [lista[:i]] + part
if ordered:
for p in partition(l, k):
yield p
else:
for p in multiset_partitions(l, k):
yield p

Categories

Resources