Python looping combinations of 8 objects into 3 groups, 3-3-2 - python

Let's say I have a list of 8 objects, numbered that 1-8.
The objects are put into three boxes, 3 in one box, 3 in another box, 2 in the last box. By mathematics, there are 8C3*5C3=560 ways to do this. I want to loop through there 560 items.
Is there any way in Python to do so?
The result should look like this:
list=['12','345',678'], ['12','346','578'], ..., etc.
Note that ['12','345','678'] and ['12','354',876'] are considered the same for this purpose.
I want to make a for-loop this list. Is there any way in Python to do so?
Here is the solution I get, but it seems ugly.
import itertools
for c1,c2 in itertools.combinations(range(8),2):
l2=list(range(8))
l2.pop(c2)
l2.pop(c1)
for c3,c4,c5 in itertools.combinations(l2,3):
l3=l2[:]
l3.remove(c5)
l3.remove(c4)
l3.remove(c3)
c6,c7,c8=l3
print(c1,c2,c3,c4,c5,c6,c7,c8)

def F(seq, parts, indexes=None, res=[], cur=0):
if indexes is None: # indexes to use for combinations
indexes = range(len(seq))
if cur >= len(parts): # base case
yield [[seq[i] for i in g] for g in res]
return
for x in combinations(indexes, r=parts[cur]):
set_x = set(x)
new_indexes = [i for i in indexes if i not in set_x]
for comb in F(seq, parts, new_indexes, res=res + [x], cur=cur + 1):
yield comb
it = F('12345678', parts=(2,3,3))
for i in range(10):
print [''.join(g) for g in next(it)]
['12', '345', '678']
['12', '346', '578']
['12', '347', '568']
['12', '348', '567']
['12', '356', '478']
['12', '357', '468']
['12', '358', '467']
['12', '367', '458']
['12', '368', '457']
['12', '378', '456']
Another example:
for c in F('1234', parts=(2,2)):
print [''.join(g) for g in c]
['12', '34']
['13', '24']
['14', '23']
['23', '14']
['24', '13']
['34', '12']

You could just permute all your 8 values (like shown on previous answers).
for that use this previous answer (also on the following code).
Then assign each combination as a tuple, so they can be hashed and unique, for that you'll have to order them, so they can also be compare uniquely.
def all_perms(elements):
if len(elements) <=1:
yield elements
else:
for perm in all_perms(elements[1:]):
for i in range(len(elements)):
#nb elements[0:1] works in both string and list contexts
yield perm[:i] + elements[0:1] + perm[i:]
v = [1,2,3,4,5,6,7,8]
a = {}
for i in all_perms(v):
k = (tuple(sorted([i[0],i[1]])) , tuple(sorted([i[2],i[3],i[4]])) , tuple(sorted([i[5],i[6],i[7]])))
if k not in a:
a[k] = [str(i[0])+str(i[1]), str(i[2])+str(i[3])+str(i[4]), str(i[5])+str(i[6]) + str(i[7])]
x = 0
for i in a.values():
print x, i
x+=1
For your example on 8 values, this gives 560 combinations.

l would be a list of eight objects, in this example strings:
l = ["O1","02","03","04","04","06","07","08"]
for group in [l[:3],l[3:6],l[6:]]: #get 3 slices of the list into 3's and a 2
print(group)
Produces:
>>>
['O1', '02', '03']
['04', '04', '06']
['07','08']

Related

How do I delete certain elements of my list?

So I've got a list that looks like this: ['87','88','89',...,'98','99'].
I want to check if there's a a number in this list that can be paired, like '89' and '98'. If there's such a number, I would want to remove it from the list and determine the length of the customised list. I've tried this but without success:
for j in range(len(list)):
if list[j][::-1] in list:
length -= 1
The customised list should look something like this: ['87','88','89',...,'97','99']
I've set the length of my list at the beginning of my code equal to 0 so my variable isn't the problem. Can someone help me with this problem? I'm pretty new to Python. Thanks in advance!
Form new list with only first elements from reverse pairs.
myList = ['87','88','89','98','99']
newList = []
for el in myList:
if el[::-1] not in newList:
newList.append(el)
print(len(newList))
Or complex check for original list
myList = ['87','88','89','98','99']
for el in myList[:]:
if el in myList and el[::-1] in myList and el != el[::-1]:
myList.remove(el[::-1])
print(len(myList))
Or only length finding. Counting each pair twice, because condition is true for first and second elements in pair.
myList = ['87','88','89','98','99']
pairsCount = 0
for el in myList[:]:
if el in myList and el[::-1] in myList and el != el[::-1]:
pairsCount += 1
print(len(myList) - pairsCount // 2)
If you still want to keep the palindromes like "88":
new_list = []
for v in my_list:
if v[::-1] == v:
new_list.append(v)
elif v[::-1] not in my_list:
new_list.append(v)
print(len(new_list))
Example
my_list = ['87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99']
Output
['87', '88', '90', '91', '92', '93', '94', '95', '96', '97', '99']
Both "89" and "98" are removed but palindromes like "88" are kept.
What I can think of is something like what follows:
myList = ['87','88','89','98','99']
[x for i,x in enumerate(myList) if x[::-1] not in myList[:i]]
Output
['87', '88', '89', '99']
Note that, in this answer, numbers like 99 will not be selected in the output since their corresponding palindromes are themselves.
And If you are interested in the length of the list, you can do something like:
len([x for i,x in enumerate(myList) if x[::-1] not in myList[:i]])
Output
4

Sort a list by presence of items in another list

Suppose I have two lists:
a = ['30', '10', '90', '1111', '17']
b = ['60', '1201', '30', '17', '900']
How would you sort this most efficiently, such that:
list b is sorted with respect to a. Unique elements in b should be placed at the end of the sorted list. Unique elements in a can be ignored.
example output:
c = ['30', '17', '60', '1201', '900']
Sorry, it's a simple question. My attempt is stuck at the point of taking the intersection.
intersection = sorted(set(a) & set(b), key = a.index)
There is no need to actually sort here. You want the elements in a which are in b, in the same order as they were in a; followed by the elements in b which are not in a, in the same order as they were in b.
We can just do this with two filters, using the sets for fast membership tests:
>>> a = ['30', '10', '90', '1111', '17']
>>> b = ['60', '1201', '30', '17', '900']
>>> a_set = set(a)
>>> b_set = set(b)
>>> [*filter(lambda x: x in b_set, a), *filter(lambda x: x not in a_set, b)]
['30', '17', '60', '1201', '900']
Or if you prefer comprehensions:
>>> [*(x for x in a if x in b_set), *(x for x in b if x not in a_set)]
['30', '17', '60', '1201', '900']
Both take linear time, which is better than sorting.
You can create a custom dictionary, with the keys being the entries in a and the values their position. Then sort b according to the values in the dictionary. You can use dict.get for the lookup and inf if the value is not present:
a = ['30', '10', '90', '1111', '17']
b = ['60', '1201', '30', '17', '900']
d = {i:ix for ix, i in enumerate(a)}
#{'30': 0, '10': 1, '90': 2, '1111': 3, '17': 4}
sorted(b, key=lambda x: d.get(x, float('inf')))
#['30', '17', '60', '1201', '900']
As you gave the hint of using set, it seems to me that the two lists contain non-duplicated items. Then you can simply do list comprehension:
c = [x for x in a if x in b] + [x for x in b if x not in a]
This is O(n^2), however. If your list is large and want to make it faster, try to build a set of a and b respectively and use them for membership check.
Your title is actually clearer than your description and can be pretty directly translated to code:
Sort a list by presence of items in another list
Code:
>>> sorted(b, key=set(a).__contains__, reverse=True)
['30', '17', '60', '1201', '900']
or
>>> sorted(b, key=lambda x, s=set(a): x not in s)
['30', '17', '60', '1201', '900']
Sorting booleans is practically indistinguishable from linear time, and these solutions are faster than the accepted solution both on your example data as well as on example data I tried with millions of random numbers (where about half of b's elements were in a).
Benchmarks
n b in a kaya1 kaya2 heap1 heap2 heap3
----------------------------------------------------------
1024 53.12% 0.00046 0.00033 0.00020 0.00067 0.00018
2048 51.03% 0.00142 0.00069 0.00048 0.00071 0.00060
4096 50.34% 0.00226 0.00232 0.00127 0.00183 0.00125
8192 50.42% 0.00938 0.00843 0.00328 0.00471 0.00351
16384 50.38% 0.02010 0.01647 0.00776 0.00992 0.00839
32768 49.96% 0.03987 0.03165 0.01661 0.02326 0.01951
65536 50.20% 0.08002 0.06548 0.03326 0.04828 0.03896
131072 50.04% 0.16118 0.12863 0.06671 0.09642 0.07840
262144 50.06% 0.32698 0.26757 0.13477 0.19342 0.15828
524288 50.08% 0.66735 0.54627 0.27378 0.38365 0.32496
1048576 50.00% 1.34095 1.08972 0.54703 0.78028 0.65623
2097152 50.03% 2.68957 2.20556 1.13797 1.60649 1.33975
4194304 50.01% 5.36141 4.33496 2.25494 3.18520 2.70506
8388608 49.99% 10.72588 8.74114 4.56061 6.35421 5.36515
Note:
n is the size of b.
a is prepared as a set before benchmarking the functions, in order to focus on their differences. The size of a is always 8388608 in order to keep in a checks constant time (even sets get slower when they get larger).
b in a is the percentage of elements of b in a. I made them so that this is about 50%.
kaya1 and kaya2 are from the accepted answer by #kaya3, modified so that they do what I think is the task (sort b by presence of items in a, not "a & b" followed by "b \ a").
heap1 and heap2 are my above two solutions using sorted.
heap3 is the fastest solution without sorted that I was able to write.
The results are times in seconds.
Benchmark code:
from timeit import repeat
import random
def kaya1(a_set, b):
return [*filter(lambda x: x in a_set, b), *filter(lambda x: x not in a_set, b)]
def kaya2(a_set, b):
return [*(x for x in b if x in a_set), *(x for x in b if x not in a_set)]
def heap1(a_set, b):
return sorted(b, key=a_set.__contains__, reverse=True)
def heap2(a_set, b):
return sorted(b, key=lambda x: x not in a_set)
def heap3(a_set, b):
not_in_a = []
append = not_in_a.append
in_a = [x for x in b if x in a_set or append(x)]
in_a.extend(not_in_a)
return in_a
print(' n b in a kaya1 kaya2 heap1 heap2 heap3')
print('----------------------------------------------------------')
A = random.sample(range(2**24), 2**23)
B = random.sample(range(2**24), 2**23)
a_set = set(A)
for e in range(10, 24):
n = 2**e
b = B[:n]
print('%7d %5.2f%%' % (n, 100 * len(set(b) & a_set) / len(b)), end='')
expect = None
for sort in kaya1, kaya2, heap1, heap2, heap3:
t = min(repeat(lambda: sort(a_set, b), number=1))
print('%9.5f' % t, end='')
output = sort(a_set, b)
if expect is None:
expect = output
else:
assert output == expect
print()
Maybe this should work.
intersection = sorted(set(a) & set(b), key=a.index)
intersection.extend([ele for ele in b if ele not in intersection])

Process a list and output as list

I'm quite new to python and have a question about processing a list with a list as result.
Example:
list1 = ["vbhg12vbdf42vbsdh24", "dbsh13vdsj24lvk48"] #must become [['12','42','24'], ['13','24','48']]
list2 = (re.findall("\d+", str(list1))) # gives ['12', '42', '24', '13', '24', '48']
See comments. Any idea how I can do this?
Much appreciated.
First of all you need to specify that your pattern is a regex in your findall() function with add r at beginning of your pattern, then you need to loop over your list and apply the function on its element,You can use a list comprehension :
>>> list1 = ["vbhg12vbdf42vbsdh24", "dbsh13vdsj24lvk48"]
>>> import re
>>> [re.findall(r'\d+',i) for i in list1]
[['12', '42', '24'], ['13', '24', '48']]
How about:
result = []
for x in list1:
result.append(re.findall("\d+", x))
Or, as a list comprehension:
result = [re.findall("\d+", x) for x in list1]

I want to move a item to first index in list. How to simplify code?

This is my code.
lst=['0','1','2','3','4']
i = lst.index('2')
lst.pop(i)
tmp=[]
tmp.append('2')
tmp.extend(lst)
lst = tmp
print lst #output:['2','0','1','3','4']
Now I want to write pretty code. I think it may be to have room for improvement.So, I hope anyone who can explain and instruct me.Thanks!
sorted([0,1,2,3,4,5], key=lambda x: x == 2, reverse=True)
As an alternative answer you can use slicing :
>>> i = lst.index('2')
>>> ['2']+lst[:i]+lst[i+1:]
['2', '0', '1', '3', '4']
You can embed it inside a function :
>>> def move(elem,l):
... i = l.index(elem)
... return [elem]+lst[:i]+lst[i+1:]
...
>>> move('2',lst)
['2', '0', '1', '3', '4']
Donig it in place (altering the list):
lst = lst.pop(lst.index(2)) and (not lst.insert(0, 2)) and lst
Creating a new list for the result:
[2] + (lst.pop(lst.index(2)) and lst)

Combine numerous lists of different length in python in an alternating order

If i have a few lists
I am able to combine list 1 with list 2 however, I have not managed to succeed to combine the other lists.
def alternator():
iets = []
for i in range(len(list2)):
something += [list1[i]]
something +=[list2[i]]
result = something
result_weaver(result)
def result(x):
list31 = list3
if len(list3) < len(x) :
while len(list31) != len(x):
list31 += '-'
I decided to add '-' in order to make sure the lengths of both lists were equal, so the for loop could go to work.
Does anyone have better ideas on how to program this ?
Use itertools.zip_longest() here:
try:
from itertools import zip_longest
except ImportError:
# Python 2
from itertools import izip_longest as zip_longest
def alternate(list1, list2):
return [v for v in sum(zip_longest(list1, list2), ()) if v is not None]
The zip_longest() call adds None placeholders (similar to your own attempt to add - characters), which we need to remove again from the sum() output after zipping.
Demo:
>>> alternate(list1, list2)
['1', '5', '2', '6', '3', '7', '8']
>>> alternate(alternate(list1, list2), list3)
['1', '9', '5', '2', '6', '3', '7', '8']

Categories