Introduction to Probability

Multivariable Calculus

Algorithms: Part II

Algorithms: Part I

Introduction to Software Design and Architecture

Calculus Two: Sequences and Series

LAFF Linear Algebra

Stanford Machine Learning

Calculus One

Computational Thinking

Effective Thinking Through Mathematics

CS50 Introduction to Computer Science

Others

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.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    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)