I am trying to find if there is a more efficient way of finding these combinations using some Python scientific library.
I am trying to avoid native for loops and list append preferring to use some NumPy or similar functionality that in theory should be more efficient given it's using C code under the hood. I am struggling to find one, but to me this is quite a common problem to make these operations in an efficient way rather than using slow Python native structures.
I am wondering if I am looking in the wrong places? E.g. this does not seem to help here: https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.random.binomial.html
See here I am taking the binomial coefficients of a list of length 5 starting from a lower bound of 2 and finding out all the possible combinations. Meanwhile I append to a global list so I then have a nice list of "taken items" from the original input list.
import itertools
input_list = ['a', 'b', 'c', 'd', 'e']
minimum_amount = 2
comb_list = []
for i in range(minimum_amount, len(input_list)):
curr_list = input_list[:i+1]
print(f"the current index is: {i}, the lists are: {curr_list}")
curr_comb_list = list(itertools.combinations(curr_list, i))
comb_list = comb_list + curr_comb_list
print(f"found {len(comb_list)} combinations (check on set length: {len(set(comb_list))})")
print(comb_list)
Gives:
found 12 combinations (check on set length: 12)
[('a', 'b'), ('a', 'c'), ('b', 'c'), ('a', 'b', 'c'), ('a', 'b', 'd'),
('a', 'c', 'd'), ('b', 'c', 'd'), ('a', 'b', 'c', 'd'), ('a', 'b', 'c', 'e'),
('a', 'b', 'd', 'e'), ('a', 'c', 'd', 'e'), ('b', 'c', 'd', 'e')]
Is it possible to do this avoiding the for loop and using some scientific libraries to do this quicker?
How can I do this in a quicker way?
The final list contains all combinations of any length from 1 to len(input_list), which is actually the Power Set.
Look at How to get all possible combinations of a list’s elements?.
You want all combinations from input_list of length 2 or more.
To get them, you can run:
comb_lst = list(itertools.chain.from_iterable(
[ itertools.combinations(input_list, i)
for i in range(2, len(input_list)) ]))
Something similiar to powerset in examples in the itertools web site,
but not exactly the same (the length starts from 2, not from 1).
Note also that curr_list in your code is actually used only for printing.
Related
I'm wondering if there is a neater way of doing part 3 of my code?
I am building a tic-tac-toe game for an assigment and this is the function for checking if there is a line of 3 of X's or O's. So I'm splitting the "board" list which represents the 3x3 gameboard into three lists that represent rows, columns and "corner to corner" lines.
def check_forwin(board,split_list):
#part 1 splice a single list into 3 lists
check_list1 = [tuple(board[i:j]) for i, j in zip([1]+ split_list, split_list)]
#part 2"invert" the list, as in choosing columns
check_list2 = list(zip(check_list1[0],check_list1[1],check_list1[2]))
#part 3 make 2 lists from "corner to corner"
check_list3 = [[],[]]
check_list3[0] = tuple([check_list1[0][0],check_list1[1][1],check_list1[2][2]])
check_list3[1] = tuple([check_list1[0][2],check_list1[1][1],check_list1[2][0]])
return
board = ["#","a","b","c","d","e","f","g","h","i"]
split_list1 = [4, 7, 10]
check_forwin(board,split_list1)
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i')]
[('a', 'd', 'g'), ('b', 'e', 'h'), ('c', 'f', 'i')]
[('a', 'e', 'i'), ('c', 'e', 'g')]
Using list comprehension makes it a bit neater, however, as Kalma, said "neat" is very subjective.
def check_forwin(board,split_list):
#part 1 splice a single list into 3 lists
check_list1 = [tuple(board[i:j]) for i, j in zip([1]+ split_list, split_list)]
#part 2"invert" the list, as in choosing columns
check_list2 = list(zip(check_list1[0],check_list1[1],check_list1[2]))
#part 3 make 2 lists from "corner to corner"
check_list3 = [[],[]]
# Number of element in row or column
n = len(check_list1)
check_list3[0] = tuple([check_list1[i][i] for i in range(n)])
check_list3[1] = tuple([check_list1[i][n-i-1] for i in range(n)])
return
Interesting question, though "neat" is a very subjective concept... What you think about this?
def check_forwin(board, board_size):
#part 1 splice a single list into 3 lists
check_list1 = [tuple(board[i + j] for i in range(board_size)) for j in range(1, len(board), board_size)]
#part 2"invert" the list, as in choosing columns
check_list2 = [tuple(check_list1[i][j] for i in range(board_size)) for j in range(board_size)]
#part 3 make 2 lists from "corner to corner"
check_list3 = [tuple(check_list1[i][i] for i in range(board_size)), tuple(check_list2[i][board_size - 1 - i] for i in range(board_size - 1, -1, -1))]
return
board = ["#","a","b","c","d","e","f","g","h","i"]
check_forwin(board, 3)
If all you want is convert a single list into list of lists, then you can use either of the methods below. However I read the instructions wrong and thought you wanted an entire game checker to be implemented. That is the answer I had posted earlier. Below dashed lines is an implementation of that. I have kept it but you may ignore the lines below "=====" if you are not interested.
Method 1 using base python
import math
def Convert_Into_SquareBoard_UsingBasePython(List):
List = List[1:]
return [ [List[i*int(math.sqrt(len(List)))+j] for j in range(3)] for i in range(0,int(math.sqrt(len(List))),1)]
This gives the following output.
[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
Method 2 using Numpy
def Convert_Into_SquareBoard_UsingNumpy(List):
return np.array(List[1:]).reshape(int(np.sqrt(len(List[1:]))),int(np.sqrt(len(List[1:])))).tolist()
This gives the same output as above
[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
======================================================================
Nice answers have already been posted. However, if numpy is allowed you can do it in another way. Basically you cast the list of lists in a numpy array and then check for Row, Column and Diagonal wins. As far as elegancy is concerned, I am not sure about that. Despite that I am still posting it to add another method for the solution.
import numpy as np
def Check_Win(a):
a = np.array(a)
##Get both diagonals
Diag_Array = np.array([a.diagonal(),np.flip(a,1).diagonal()])
##Check 1 for row win
if np.any(np.apply_along_axis(lambda x: len(set(x))==1, 1, a)):
return True
##Check 2 for column win
elif np.any(np.apply_along_axis(lambda x: len(set(x))==1, 0, a)):
return True
##Check 3 along diagonals
elif np.any(np.apply_along_axis(lambda x: len(set(x))==1, 1, Diag_Array)):
return True
return False
a_No_Win = [('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i')]
a_Row_Win = [('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'g', 'g')]
a_Column_Win = [('h', 'b', 'c'), ('h', 'e', 'f'), ('h', 'h', 'i')]
a_Diag_1_Win = [('a', 'b', 'c'), ('d', 'a', 'f'), ('g', 'h', 'a')]
a_Diag_2_Win = [('a', 'b', 'h'), ('d', 'h', 'f'), ('h', 'h', 'i')]
print(Check_Win(a_No_Win))
print(Check_Win(a_Row_Win))
print(Check_Win(a_Column_Win))
print(Check_Win(a_Diag_1_Win))
print(Check_Win(a_Diag_2_Win))
This gives the outputs as expected below.
False
True
True
True
True
Added lines below after realising that the input is a flat list and not a list of lists and the first element is the character to look out for
For above method to work you will have to first get the list of lists. Also it does not look for a specific character. As long as any character is present in a consecutive manner it will register a win. I think you are not looking for that.
I think you have a single list and the first element of the list is the character that you are looking for the win. All other characters you are ignoring. Below is the modified version of the above function. It has been changed slightly to "perhaps" suit your needs.
import numpy as np
def Check_Win(a):
CheckSymbol = a[0]
a = np.array(a[1:]).reshape(int(np.sqrt(len(a[1:]))),int(np.sqrt(len(a[1:]))))
##Get both diagonals
Diag_Array = np.array([a.diagonal(),np.flip(a,1).diagonal()])
##Check 1 for row win
if np.any(np.apply_along_axis(lambda x: list(x).count(CheckSymbol)==int(np.sqrt(len(a[1:]))), 1, a)):
return True
##Check 2 for column win
elif np.any(np.apply_along_axis(lambda x: list(x).count(CheckSymbol)==int(np.sqrt(len(a[1:]))), 0, a)):
return True
##Check 3 along diagonals
elif np.any(np.apply_along_axis(lambda x: list(x).count(CheckSymbol)==int(np.sqrt(len(a[1:]))), 1, Diag_Array)):
return True
return False
a_No_Win = ["#","a","b","c","d","e","f","g","h","i"]
a_Row_Win = ["#","a","b","c","d","e","f","#","#","#"]
a_Column_Win = ["#","#","b","c","#","e","f","#","h","i"]
a_Diag_1_Win = ["#","#","b","c","d","#","f","g","h","#"]
a_Diag_2_Win = ["#","a","b","#","d","#","f","#","h","i"]
print(Check_Win(a_No_Win))
print(Check_Win(a_Row_Win))
print(Check_Win(a_Column_Win))
print(Check_Win(a_Diag_1_Win))
print(Check_Win(a_Diag_2_Win))
The modified output is as below and is as expected.
False
True
True
True
True
Please let me know if this was of help.
I'm doing implement on Apriori algorithm at the moment I am stuck to create 3 set of word
Suppose I have list of 2 words like this
FI2 = [('a','b'),('a','c'),('a','d'),('b','d'),('b','e'),('e','f')];
First approach I did with by distinct all element into 1 word and using itertools.combinations of 3 which is the compute expesive and not right approach since the result should be subset from C2
It should be like this result
C3 = [('a','b','c'),('a','b','d'),('a','c','d'),('b','d','e')]
I am having a problem how to approach this problem. I would be appreciate how to give me some guideline how to do this one
any chance C3 is missing some values? ('b','e','f'), ('a','b','e')
im sure it's not the best way but its a start:
from itertools import combinations
FI2 = [('a','b'),('a','c'),('a','d'),('b','d'),('b','e'),('e','f')]
# check if two tuples have at least one var in common
check_intersection = (lambda c: len(set(c[0]).intersection(set(c[1]))) > 0)
# run on all FI2 pairs combinations
# if two tuples have at least one var in common, a merged tuple is added
# remove the duplicates tuples from the new list
C3 = list(set([tuple(sorted(set(c[0] + c[1])))for c in combinations(FI2,2) if check_intersection(c)]))
print(C3)
#=> [('b', 'd', 'e'), ('a', 'b', 'e'), ('b', 'e', 'f'), ('a', 'b', 'd'), ('a','c','d'), ('a', 'b', 'c')]
Maybe it's not so simple, but I am trying to essentially find all the permutations of a list of letters.
[[a,b],[c,d],[e,f]] for simplicity as it can be longer than just 3 lists of 2 (ie 6 lists of 3 letters, etc.).
I want my program to find all 8 combinations for above example while maintaining order of the main list (is that permutation?).
ace
acf
ade
adf
bce
bcf
bde
bdf
Currently I think the solution below will iterate recursively through the combinations I want; however, I cannot figure out how to store them in order because when it reaches the base condition for the first row it will simply go to the next letter in the last index of the list.
I don't believe I was able to find something that would work for me in itertools
def find_comb(mylist):
for curr_index in range(0,len(mylist)):
for letter in mylist[curr_index]:
if (curr_index+1<=len(mylist)):
next_letter=find_comb(mylist[curr_index+1:])
return 1 #wrote 1 for now because I am stumped
I think what you want is itertools.product
from itertools import product
x = [['a','b'], ['c','d'], ['e','f']]
for _ in product(*x):
print _
Prints
('a', 'c', 'e')
('a', 'c', 'f')
('a', 'd', 'e')
('a', 'd', 'f')
('b', 'c', 'e')
('b', 'c', 'f')
('b', 'd', 'e')
('b', 'd', 'f')
Regarding your comment:
product takes a bunch of iterables and generates their product, however, in your case you were sending it a single iterable (that consisted of more iterables). So instead of passing in l1, l2, l3 you were passing in[l1, l2, l3].
To actually pass in the three iterables, we have to unpack the list using the asterisk, which will turn that single list into three arguments. For more on that, see What does ** (double star) and * (star) do for parameters?
I want to create a list of possible combinations of a list of lists (example will explain better)
list=[[a,b],[c,d],[f]]
The result should be
acf
adf
bcf
bdf
The length of the list can vary, and the length of the lists within the variable list can also vary. How would I make this/these loop(s) programmatically? (preferably explained in Python or pseudo-language)
That's what itertools.product is for:
>>> lst = ['ab','cd','f']
>>> from itertools import product
>>> list(product(*lst))
[('a', 'c', 'f'), ('a', 'd', 'f'), ('b', 'c', 'f'), ('b', 'd', 'f')]
import itertools
list=[['a','b'],['c','d'],['f']]
for comb in itertools.product(*list):
print ''.join(comb)
You can do it recursively:
def printCombos(arrays, combo):
if len(arrays) == 0:
print combo
else:
for i in arrays[0]:
combo.append(i)
printCombos(arrays[1:], combo)
combo.pop()
l=[['a','b'],['c','d'],['f']]
printCombos(l, [])
curlist = []
for firstobj in listoflists[0]:
for secondobj in listoflists[1]:
for lastobj in listoflists[2]:
curlist.append(firstobj)
curlist.append(secondobj)
curlist.append(lastobj)
print ','.join(curlist)
I am trying to find all permutations from a list that are the same size or smaller than the list.
For example:
>>>allPermutations([a,b])
[[a,b], [b,a], [a], [b]]
This is the iterative code I currently have in python. I'm not sure how efficient it currently is.
import itertools
def getAllPossibleSubSchedules( seq ):
fullSet = set()
curSet = set()
curSet.add(tuple(seq))
for i in reversed(range(1, len(seq) + 1)):
permutations = set()
for curTuple in curSet:
permutationsList = list(itertools.permutations(curTuple, i))
for permutation in permutationsList:
permutations.add(permutation)
curSet = set()
for permutation in permutations:
curSet.add(permutation)
fullSet.add(permutation)
return fullSet
I'm pretty sure the algorithm will produce the summation of n! from 1 -> n permutations which grows pretty quickly. So far I have created a recursive way to do it that is incredibly slow because it does many repeated operations. I have been trying to do it through iteration but I can't figure out how to limit repeated operations. I am using python but psuedo-code would also help me a lot. Any help would be appreciated. Thanks in advance!
The following should work:
from itertools import permutations
def allPermutations(seq):
return (x for i in range(len(seq),0,-1) for x in permutations(seq, i))
For example:
>>> list(allPermutations('abc'))
[('a', 'b', 'c'), ('a', 'c', 'b'), ('b', 'a', 'c'), ('b', 'c', 'a'), ('c', 'a', 'b'), ('c', 'b', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'c'), ('c', 'a'), ('c', 'b'), ('a',), ('b',), ('c',)]
Maybe iterate over all permutations for lists with all possible sizes. To clarify:
def all_permutations(input_list):
for i in xrange(len(input_list)):
sublist = input_list[:i]
for variant in permutations(sublist):
yield variant
I am pretty sure your permutations.add() and curSet.add() and fullSet.add() calls are going to cause your routine to crawl to a halt very quickly. If you keep changing the size of an array, the memory allocated will "run out of space" and a new location has to be found. This means that the entire array gets copied. And then you add another element - rinse and repeat.
You need to compute the number of elements you need, and pre-allocate the space for that. Thus, if you have 5 elements, you need to allocate ( 5! + 5*4! + 10*3! + 10*2! + 5 ) x 5 elements for the final result - and less for the intermediate results. Then you fill these array without shuffling blocks of memory around; it will make things significantly faster.