Power Set Functions #
Use the position of
1
in bites to calculate all the possibilities of combinations in a list.like five bits
0 0 0 0 0
to indicate all the items’ positions, and1
can be every position in the bits. All the combinations should be the Powerset we want to, then use>>
to check every combination’s position whether it is 1 or not.def powerSet(items): N = len(items) # enumerate the 2**N possible combinations for i in range(2**N): combo = [] for j in range(N): # test bit jth of integer i # >>j. move the bit we want to check to the end # %2. remove all the other bits execpt the last one # check the one we kept if it is 1 not 0, # which means we want to keep the item which on the position # example: 0 1 1 0 1 # we want to check the third "1" # first move the second bit to the end(>>j), will be "0 0 0 1 1" # then remove all the other bits(%2), we got "0 0 0 0 1" # compare it with 1, which is true, # so we take the item with the position, which will be item[2] if (i >> j) % 2 == 1: combo.append(items[j]) yield combo
Use
itertools.combinations
to calculate r length subsequences of elements, then iterate with different length.the function of
combinations
is a little complicated, added some notations to help me to understand.from itertools import chain def powerset_generator(sets): for subset in chain.from_iterable(combinations(sets, r) for r in range(len(sets)+1)): yield subset # the logic of this function is # set a new array with length r # loop the last element's index from i to i+n-r(n is the length of pool, r is the length of subsequence). # when hit the maximum which should be n-1, increase the last-1 element's index. # loop until the first element's index hit the maximum, # then increase the previous index, and set the last index to previous index + 1, # then back to the loop until all of the indices hit the maximum # For example: iterable = [1,2,3,4,5], r = 3 # (1, 2, 3) # (1, 2, 4) # (1, 2, 5) <-- the last index hit the maximum # (1, 3, 4) <-- increase the previous index, and set every one after to previous index + 1, # (1, 3, 5) # (1, 4, 5) <-- the (last-1) index hit the maximum # (2, 3, 4) # (2, 3, 5) # (2, 4, 5) # (3, 4, 5) <-- the (last-2) index hit the maximum def combinations(iterable, r): pool = tuple(iterable) n = len(pool) if r > n: return indices = list(range(r)) # In the "while" circle, we will start to change the indices by adding 1 consistently. # So yield the first permutation before the while start. yield tuple(pool[x] for x in indices) while True: # This 'for' loop is checking whether the index has hit the maximum from the last one to the first one. # if it indices[i] >= its maximum, # set i = i-1, check the previous one # if all of the indices has hit the maximum, # return the function for i in reversed(range(r)): # let's take an example to explain why using i + n - r # pool indices: [0,1,2,3,4] # subsequence indices: [0,1,2] # so # indices[2] can be one of [2,3,4], # indices[1] can be one of [1,2,3], # indices[0] can be one of [0,1,2], # and the range of every index is n-r, like this sample uses: 5-3=2 # then # indices[2] < 2+2 = i+2 = i+n-r, # indices[1] < 1+2 = i+2 = i+n-r, # indices[0] < 0+2 = i+2 = i+n-r, # this can also be: indices[i] != i + n - r if indices[i] < i + n - r: break else: # loop finished, return return # Add one for current indices[i], # (we already yield the first permutation before the loop) indices[i] += 1 # this for loop increases every indices which is after indices[i]. # cause, current index has increased, and we need to confirm every one behind is initialized again. # For example: current we got i = 2, indices[i]+1 will be 3, # so the next loop should start with [1, 3, 4], not [1, 3, 3] for j in range(i+1, r): indices[j] = indices[j-1] + 1 yield tuple(pool[x] for x in indices)