我正在解决涉及组合的编程难题.这让我有了一个很棒的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)
这次演习非常简洁,我怀疑这是所有魔法发生的地方.请给我一个提示,这样我就可以搞清楚.
循环有两个目的:
假设您有一个超过 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 - r是i索引列表中索引的最大值:
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)
=>
Run Code Online (Sandbox Code Playgroud)for i in reversed(range(r)): if indices[i] != i + n - r: break else: break
这在当前索引列表中向后循环,并在不包含其最大索引值的第一个位置处停止。如果所有位置都保持其最大索引值,则没有进一步的索引列表,因此return。
在一般情况下,[0,1,4]可以验证下一个列表应该是[0,2,3]。循环停在位置1,后续代码
Run Code Online (Sandbox Code Playgroud)indices[i] += 1
增加indeces[i]( 1 -> 2)的值。最后
Run Code Online (Sandbox Code Playgroud)for j in range(i+1, r): indices[j] = indices[j-1] + 1
将所有头寸重置> i为最小的合法索引值,每个都1大于其前任。
| 归档时间: |
|
| 查看次数: |
1633 次 |
| 最近记录: |