I was solving a programming puzzle involving combinations. It led me to wonderful itertools.combinations function and I'd like to know how it works under the hood. Documentation says that the algorithm is roughly equivalent to the following:
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)
I got the idea: we start with the most obvious combination (r first consecutive elements). Then we change one (last) item to get each subsequent combination.
The thing I'm struggling with is a conditional inside for loop.
for i in reversed(range(r)):
if indices[i] != i + n - r:
break
It's very terse, and I suspect that's where all the magic happens. Please, give me a hint so I could figure it out.
Update: I've got a grasp of the idea in question. Now, to fully understand this algorithm, I'm going to prove its corectness mathematically. I'd be thankful if you could help me with the proof (where to start?).
elsefrom happening. – tobias_k 3 hours ago