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, and 1 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)
      

results matching ""

    No results matching ""