Python deduce best number among list of lists [closed] - python

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]

Related

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

Create unique combinations regardless of subset size

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

Why is a different outcome occurring on the following use of sort

I was doing a puzzle and where i had to add 2 lists having same length to a new list and sort the list by the second element of the list.
for x in range(n):
tmp.append([start[x],end[x]])
where start and end are lists containing equal elements and n is the length of start and end.
Now, idk why a difference / error occurs between the use of following code.
end.sort()
for x in range(n):
tmp.append([start[x],end[x]])
and
for x in range(n):
tmp.append([start[x],end[x]])
tmp.sort(key=lambda x:x[1])
EDIT:-
Input list
start=[1, 3, 0, 5, 8, 5]
end=[2, 4, 6, 7, 9, 9]
output by sorting first
[[1, 2], [3, 4], [0, 6], [5, 7], [8, 9], [5, 9]]
output by sorting later
[[1, 2], [3, 4], [0, 6], [5, 7], [8, 9], [5, 9]]
works fine for this list but doesn't work for a bigger array
(array contains 80 elements thats why not uploading here)
If you sort end first, you combine the original order of start with the sorted order of end.
If you combine the two lists first and then sort by the end element, the start elements will get reordered, too, as they "tag along" with their end partner. Consider
start = [1, 2, 3]
end = [3, 2, 1]
Now, sorting end and combining, you'll end up with:
start = [1, 2, 3]
end = [1, 2, 3]
# =>
tmp = [[1, 1], [2, 2], [3, 3]]
Combining first, however, produces:
tmp = [[1, 3], [2, 2], [3, 1]]
And sorting this by the second element, will shuffle the old start elements as well:
tmp.sort(key=lambda x:x[1])
# [[3, 1], [2, 2], [1, 3]]
Side note: Check out zip:
tmp = list(zip(start, end))

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

Saving a key from a dictionary without value

I am representing a weighted graph as a dictionary where the key represents a vertex and the following lists represent the edges incident on the vertex (first number weight of edge and second number adjacent vertex):
wGraph = { 1 : [[2, 2],[3, 4],[3, 3]],
3 : [[3, 2],[5, 4],[7, 5]],
2 : [[2, 1],[4, 4],[3, 6]],
4 : [[3, 1],[4, 2],[1, 6],[5, 3]],
6 : [[3, 2],[1, 4],[8, 5]],
5 : [[7, 3],[8, 6],[9, 7]],
7 : [9, 5]}
I would like to save a random key without its value from the dictionary to a list called visited.
random_num = random.randrange(len(wGraph))
visited = []
How can I get the key from the dictionary according to the random number?
To make a random choice from some values, use random.choice. You want to choose from the keys of the dictionary, so that's exactly what you pass in:
random.choice(wGraph.keys())
The answer depends on the details -- how many keys do you have, are they always numbers 1...max, do you want to revisit keys you've already visited before, are graph nodes added over time or do they stay constant, will you eventually visit all keys or only a small percentage of them, and so on.
One idea: if your keys are always numbered sequentially as in your example, you can just use random_num + 1. You already know they are the keys of the dict and you don't have to construct anything special.
Another option is to randomize the key list just once:
>>> keys_to_visit = wGraph.keys()
>>> keys_to_visit.shuffle()
>>> first_key_to_visit = keys_to_visit.pop()
And just keep calling .pop() whenever you need a new one. This only works if you don't want to revisit nodes and the node list is constant.
import random
visited = []
def random_key():
wGraph = {1: [[2, 2], [3, 4], [3, 3]],
3: [[3, 2], [5, 4], [7, 5]],
2: [[2, 1], [4, 4], [3, 6]],
4: [[3, 1], [4, 2], [1, 6], [5, 3]],
6: [[3, 2], [1, 4], [8, 5]],
5: [[7, 3], [8, 6], [9, 7]],
7: [9, 5]}
random_key = random.choice(wGraph.keys())
if random_key not in visited:
visited.append(random_key)
print visited
for e in range(7):
random_key()

Categories

Resources