递归解法

def comb(l, k, j, m, res):
    # we j start at 0
    n = len(l)
    if j >= len(l):
        print(list(filter(None, res)))
        return
    if m < k:
        res[j] = l[j]
        comb(l, k, j + 1, m + 1, res)
    if (k - m) < (n - j):
        res[j] = None
        comb(l, k, j + 1, m, res)

comb([1, 2, 3, 4, 5], 3, 0, 0, [None for i  in range(5)])

迭代解法

通用的打印方法

def print_res(l, res, k):
    ans = [l[res[i] - 1] for i in range(1, k+1)]
    print(ans)

 

def comb_iter1(l, k, j, res):
    n = len(l)
    while True:
        j = k
        print_res(l, res, k)
        while res[j] == n - k + j:
            j = j - 1
        res[j] = res[j] + 1
        if j == 0:
            break
        for i in range(j + 1, k + 1):
            res[i] = res[i-1] + 1

comb_iter1([1, 2, 3, 4, 5], 3, 3, list(range(0, 5)))

书中说 : The while loop of Algorithm  can be eliminated by making j a global variable (initialized to k) and observing that the rightmost changeable position is k if res[k] < n and is one less than its previous value if res[k] = n.

优化版:

def comb_iter2(l, k, j, res):
    # init item in res < 0
    n = len(l)
    while True:
        print_res(l, res, k)
        res[j] = res[j] + 1 # 直接开始加 
        for i in range(j + 1, k + 1):
            res[i] = res[i-1] + 1
        if j == 0:
            break
        if res[k] < n:
            j = k  # < 5 说明可以继续从这里加
        else:
            # 加到 5 之后,第一位需要舍弃 需要在下一轮 + 1
            j = j - 1

comb_iter2([1, 2, 3, 4, 5], 3, 3, list(range(0, 5)))

 

问题:

1. 为什么要传 list(range(0, 5))?

2. res[i] = res[i-1] + 1 是在做什么?

3. res[j] = res[j] + 1 是在做什么?

不准备做详细解释。如果对照输出和课本理解此算法,自己会发现其精妙之处。

 

 

jiamo post at 2021-06-30