Continuing with our journey into recursive procedures, different ways of selecting “k” items from “n” distinct items is an another fascinating procedure. In mathematical terms, it is denoted by nCk (read as n C k), combinations of “k” items from “n” (distinct) items.
If it is to just compute the total number of such different combinations or selections possible, mathematics as usual readily provides the recursive relation and also the terminating condition:
nCk = n-1Ck + n-1Ck-1, n > k > 0 (recursive relation)
nCk = 1, for k = n, or k = 0 (the two extremes – terminating condition)
Note that, the implicit condition on which nCk is defined is n >= k >= 0.
A straight away functional recursive implementation would look like this:
combinations(n, k) { if ((k == 0) || (k == n)) return 1; else return combinations(n - 1, k) + combinations(n - 1, k - 1); }
What about printing all the different possible selections of “k” items from “n” (distinct) items? Should be similar, as the recursive logic should still hold. Though, in the two terminating conditions, the interpretation would be slightly different. So as to say, for k = 0, i.e. selecting 0 items from “n” (distinct) items, the only way is selecting no items, and hence print nothing. However, for k = n, i.e. selecting “n” items from “n” (distinct) items, the only way is selecting all the items, and hence print everything. Additionally, the basket of “n” (distinct) items also need to be provided as an input to the procedure, say something like selections(n, k, basket[]). So, the procedure statement can be specified as follows: Implement a procedure selections(n, k, basket[]), which takes two numbers “n” & “k”, and a basket of “n” items and prints the nCk possible selections of “k” items from the “n” (distinct) items in the basket, one selection per line.
Now, with all the background set to implement selections(n, k, basket[]), let’s apply the usual trick of assuming the procedure selections() to be already existing for a lower order. What could be the lower order here? Two possibilities, as per the earlier mathematical recursive relation:
- selections(n – 1, k, basket[] (with n – 1 items)), which prints the n – 1Ck possible selections of “k” items from the “n – 1” (distinct) items in the basket, one selection per line.
- selections(n – 1, k – 1, basket[] (with n – 1 items)), which prints the n – 1Ck – 1 possible selections of “k – 1” items from the “n – 1” (distinct) items in the basket, one selection per line.
Now, how to make “n” items to “n – 1” items in selections(n, k, basket[]), so as to apply these two assumed procedures to implement the logic for “n” items? For that also, there are two possible ways, corresponding to the above two assumed procedures:
- Assume that the first item in the basket is not part of the selection, and hence print n – 1Ck possible selections of “k” items from the remaining “n – 1” (distinct) items in the basket, one selection per line.
- Assume that the first item in the basket is indeed part of the selection, and hence print n – 1Ck – 1 possible selections of “k – 1” items from the remaining “n – 1” (distinct) items in the basket, one selection per line, each prefixed by the first item in the basket.
Thus the recursive logic sounds straight forward, and it should be just a matter of invoking the two assumed procedures. However, before proceeding forward to implement, a little closer look, reveals that the second possible way of selections(n – 1, k – 1, basket[]), expects an additional requirement than what was assumed: “…, each prefixed by the first item in the basket”. And clearly the assumed procedure cannot do it (in the current state). One may think, that before the invocation of the second assumed selections(n – 1, k – 1, basket[]), the first item may be printed, so as to create the prefix of the output from the selections(n – 1, k – 1, basket[]). But think again, and the realisation will dawn that the output from selections(n – 1, k – 1, basket[]) outputs n – 1Ck – 1 lines (not just one), each of which need to be prefixed by the first item.
That sounds tricky. How to achieve the prefixing? What if the prefixing is to be assumed to be done by the selections() procedure itself. Yes. Why not? But then it calls for a change to the selections() procedure logic itself, and it would also need the prefix to be passed to it as an input. Okay then. Let’s redefine our procedure statement. Implement a procedure selections(n, k, basket[], plen, prefix[]), which takes two numbers “n” & “k”, a basket of “n” items, a prefix of “plen” items and prints the nCk possible selections of “k” items from the “n” (distinct) items in the basket, one selection per line, each prefixed by “plen” items in prefix.
And that calls for the reworking of the assumption of the procedure selections() to be already existing for a lower order, which would now be as follows:
- selections(n – 1, k, basket[] (with n – 1 items), plen, prefix), which prints the n – 1Ck possible selections of “k” items from the “n – 1” (distinct) items in the basket, one selection per line, each prefixed by “plen” items in prefix.
- selections(n – 1, k – 1, basket[] (with n – 1 items), plen, prefix), which prints the n – 1Ck – 1 possible selections of “k – 1” items from the “n – 1” (distinct) items in the basket, one selection per line, each prefixed by “plen” items in prefix.
Note that the lower ordering need not be applied to “plen” & prefix, as they are just the supporting parameters. In fact, they could be anything.
Now, these two assumed procedures can be accordingly applied as follows to implement the logic for selections(n, k, basket[], plen, prefix[]):
- Assume that the first item in the basket is not part of the selection, and hence print n – 1Ck possible selections of “k” items from the remaining “n – 1” (distinct) items in the basket, one selection per line, each prefixed by “plen” items in prefix.
- Assume that the first item in the basket is indeed part of the selection, and hence print n – 1Ck – 1 possible selections of “k – 1” items from the remaining “n – 1” (distinct) items in the basket, one selection per line, each prefixed by “plen” items in prefix and the first item in the basket. Doesn’t this first item still look to be an odd one out. Not really. Note that the first item in the basket can be made last item of the prefix, making it of length “plen + 1”, as those two parameters could be anything.
Cool. Also, both the terminating conditions, now need to print the prefix as well.
Hence, the final logic may be summarized as follows:
selections(n, k, basket[], plen, prefix[]) { if (k == 0) { print(plen, prefix); print_nl(); // print new line } else if (k == n) { print(plen, prefix); print(n, basket); print_nl(); // print new line } else { selections(n - 1, k, basket + 1, plen, prefix); prefix[plen] = basket[0]; selections(n - 1, k - 1, basket + 1, plen + 1, prefix); } }
Okay. Recursive logic done. But now, what to pass for plen and prefix, when calling the selections(), at the topmost level? That is simple. At the topmost level, in printing of the nCk possible selections of “k” items from the “n” (distinct) items in the basket, one selection per line, there is no prefix required to be printed. So, pass “plen” as 0, and an empty prefix, capable of holding at max “k” items, as that would be the max prefix possible while selecting “k” items. And to make the toplevel call look beautiful, devoid of these seemingly redundant plen and prefix, a wrapper print_selections() could be provided as follows, which may also check for the invalid values of “n” & “k”, not satisfying n >= k >= 0 (if desired):
print_selections(n, k, basket[]) { plen = 0; prefix[] = {}; // prefix being capable of supporting max "k" items selections(n, k, basket, plen, prefix); }
Note: The logic above would print the selections from right to left. What would you need to print it from left to right? Post your thoughts in the comments.