Python中itertools.combinations的算法

Bas*_* C. 11 python algorithm

我正在解决涉及组合的编程难题.这让我有了一个很棒的itertools.combinations功能,我想知道它是如何工作的.文档说该算法大致相当于以下内容:

def combinations(iterable, r):
    # combinations('ABCD', 2) --> AB AC AD BC BD CD
    # combinations(range(4), 3) --> 012 013 023 123
    pool = tuple(iterable)
    n = len(pool)
    if r > n:
        return
    indices = list(range(r))
    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != i + n - r:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1
        yield tuple(pool[i] for i in indices)
Run Code Online (Sandbox Code Playgroud)

我明白了:我们从最明显的组合(r第一个连续的元素)开始.然后我们更改一个(最后一个)项目以获得每个后续组合.

我正在努力的是一个有条件的内部for循环.

for i in reversed(range(r)):
    if indices[i] != i + n - r:
        break
Run Code Online (Sandbox Code Playgroud)

这次演习非常简洁,我怀疑这是所有魔法发生的地方.请给我一个提示,这样我就可以搞清楚.

sch*_*ggl 5

循环有两个目的:

  1. 如果已到达最后一个索引列表,则终止
  2. 确定可以合法增加的索引列表中最右边的位置。该位置然后是将所有索引向右重置的起点。

假设您有一个超过 5 个元素的可迭代对象,并且想要长度为 3 的组合。您本质上需要的是生成索引列表。上述算法的重要部分从当前的索引列表生成下一个这样的索引列表:

# obvious 
index-pool:       [0,1,2,3,4]
first index-list: [0,1,2]
                  [0,1,3]
                  ...
                  [1,3,4]
last index-list:  [2,3,4]
Run Code Online (Sandbox Code Playgroud)

i + n - ri索引列表中索引的最大值:

 index 0: i + n - r = 0 + 5 - 3 = 2 
 index 1: i + n - r = 1 + 5 - 3 = 3
 index 2: i + n - r = 2 + 5 - 3 = 4
 # compare last index-list above
Run Code Online (Sandbox Code Playgroud)

=>

for i in reversed(range(r)):
    if indices[i] != i + n - r:
        break
else:
    break
Run Code Online (Sandbox Code Playgroud)

这在当前索引列表中向后循环,并在不包含其最大索引值的第一个位置处停止。如果所有位置都保持其最大索引值,则没有进一步的索引列表,因此return

在一般情况下,[0,1,4]可以验证下一个列表应该是[0,2,3]。循环停在位置1,后续代码

indices[i] += 1
Run Code Online (Sandbox Code Playgroud)

增加indeces[i]( 1 -> 2)的值。最后

for j in range(i+1, r):
    indices[j] = indices[j-1] + 1
Run Code Online (Sandbox Code Playgroud)

将所有头寸重置> i为最小的合法索引值,每个都1大于其前任。