泻药。
大略看了一眼。
第一个元素有 n 种可能,第二个元素有 n-1 种可能,以此类推。
而观察可知 cycles 就是个 (n, n-1, n-2, ..., n-r+1) 进制的变进制数,因此 cycles 就是个计数器,直接对应到 “正在枚举输出的元素是哪个”。只不过这个变进制数的倒序对应了permutation的正序。
再注意到它给出的样例顺序非常朴素,永远是下标小的元素优先枚举,不是什么精妙的枚举顺序。带着这个信息我们再看 indices,很容易明白:cycles 为 (n, n-1, n-2..) 时,indices 为 (0, 1, 2, ..., n-1);
cycles 为 (n-1, n-1, n-2, ...) 时, indices 为 (1, 0, 2, ..., n-1); // 交换了下标为 i=0 和 j=-(n-1) 的两个元素
cycles 为 (n-2, n-1, n-2, ...) 时, indices 为 (2, 0, 1, ..., n-1); // 交换了下标为 i=0 和 j=-(n-2) 的两个元素
等等
因此 indices 就是记录一下朴素枚举顺序下的“当前排列”。
最后,注意你看的这份代码并非 python 标准库代码,而是一份为了方便理解而简化的代码。因为它说了:
Roughly equivalent to
至于这么一份为了简化的代码为什么非要用倒序枚举去做一个正序的事情,然后还时不时用 -j 去从右往左找元素(但是序列长度已知,所以从左往右找元素并不麻烦……),我觉得挺奇怪的。
EDIT: 再解释一句吧,那个代码可以看成下列递归程序的强行非递归版(省去了一些 boilerplate),注意我的版本里几乎每句话都来自原始代码,只是拆成了递归。
(cycles 哪里去了?cycles 就是栈上所有的 j)
def _perm(indices, i, n, r):
if i == r:
yield indices[:r]
else:
for j in range(n-i, 0, -1):
indices[i], indices[-j] = indices[-j], indices[i]
for result in _perm(indices, i+1, n, r):
yield result
indices[i:] = indices[i+1:] + indices[i:i+1]
def permutations(n, r):
indices = list(range(n))
return _perm(indices, 0, n, r)