Combinatorial: Difference between revisions
No edit summary |
mNo edit summary |
||
(9 intermediate revisions by 2 users not shown) | |||
Line 4: | Line 4: | ||
<table border="0" cellpadding="5" cellspacing="0" summary=""> | <table border="0" cellpadding="5" cellspacing="0" summary=""> | ||
<tr> | <tr> | ||
<td valign="top"><apll> | <td valign="top"><apll>Z←a‼R</apll></td> | ||
<td></td> | <td></td> | ||
<td></td> | <td></td> | ||
Line 13: | Line 13: | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td><apll> | <td><apll>R</apll> is a non-negative numeric scalar or one- or two-element vector. If <apll>R</apll> has only one element, it is treated as if the value were duplicated as in <apll>2⍴R</apll>. For convenience in the description below, the two elements are referred to as <apll>M</apll> and <apll>N</apll> as in <apll>(M N)←2⍴R</apll>.</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td><apll>a</apll> is a non-negative numeric scalar or one- or two- element vector which serves as the '''Selector''' for the Twelve Combinatorial Functions. | <td><apll>a</apll> is a non-negative numeric scalar or one- or two- element vector which serves as the '''Selector''' for the Twelve Combinatorial Functions. | ||
The first element ('''Function Selector''') in <apll>a</apll> is a non-negative integer for each of the Twelve functions (where the number is written here with three digits to emphasize that each digit has a [[#The_Twelvefold_Way|separate meaning]]):<br /> | The first element ('''Function Selector''') in <apll>a</apll> is a non-negative integer for each of the Twelve functions (where the number is written here as a decimal number with three digits to emphasize that each digit has a [[#The_Twelvefold_Way|separate meaning]]):<br /> | ||
<table border="0" cellpadding="1" cellspacing="0" rules="none" summary="" style="margin-left: 20px;"> | <table border="0" cellpadding="1" cellspacing="0" rules="none" summary="" style="margin-left: 20px;"> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase000/100|<apll>000</apll>]]</td> | <td>[[CombinatorialCase000/100|<apll>000</apll>]]</td> | ||
<td> [[CombinatorialCase000/100|<apll> | <td> [[CombinatorialCase000/100|<apll>M</apll> Pigeons in <apll>N</apll> holes]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase001|<apll>001</apll>]]</td> | <td>[[CombinatorialCase001|<apll>001</apll>]]</td> | ||
<td> [[CombinatorialCase001|Partitions of the number <apll> | <td> [[CombinatorialCase001|Partitions of the number <apll>M</apll> into no more than <apll>N</apll> parts]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase002|<apll>002</apll>]]</td> | <td>[[CombinatorialCase002|<apll>002</apll>]]</td> | ||
<td> [[CombinatorialCase002|Partitions of the number <apll> | <td> [[CombinatorialCase002|Partitions of the number <apll>M</apll> into <apll>N</apll> parts]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase010|<apll>010</apll>]]</td> | <td>[[CombinatorialCase010|<apll>010</apll>]]</td> | ||
<td> [[CombinatorialCase010|<apll> | <td> [[CombinatorialCase010|<apll>M</apll> Combinations of <apll>N</apll> items]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase011|<apll>011</apll>]]</td> | <td>[[CombinatorialCase011|<apll>011</apll>]]</td> | ||
<td> [[CombinatorialCase011|<apll> | <td> [[CombinatorialCase011|<apll>M</apll> Multisets of <apll>N</apll> items]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td valign="top">[[CombinatorialCase012|<apll>012</apll>]]</td> | <td valign="top">[[CombinatorialCase012|<apll>012</apll>]]</td> | ||
<td> [[CombinatorialCase012|Compositions of the number <apll> | <td> [[CombinatorialCase012|Compositions of the number <apll>M</apll> into <apll>N</apll> parts]]<br /> | ||
a.k.a. Partitions of the number <apll> | a.k.a. Partitions of the number <apll>M</apll> into <apll>N</apll> ordered parts</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase000/100|<apll>100</apll>]]</td> | <td>[[CombinatorialCase000/100|<apll>100</apll>]]</td> | ||
<td> [[CombinatorialCase000/100|<apll> | <td> [[CombinatorialCase000/100|<apll>M</apll> Pigeons in <apll>N</apll> holes]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase101|<apll>101</apll>]]</td> | <td>[[CombinatorialCase101|<apll>101</apll>]]</td> | ||
<td> [[CombinatorialCase101|Partitions of the set <apll>{ | <td> [[CombinatorialCase101|Partitions of the set <apll>{⍳M}</apll> into no more than <apll>N</apll> parts]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase102|<apll>102</apll>]]</td> | <td>[[CombinatorialCase102|<apll>102</apll>]]</td> | ||
<td> [[CombinatorialCase102|Partitions of the set <apll>{ | <td> [[CombinatorialCase102|Partitions of the set <apll>{⍳M}</apll> into <apll>N</apll> parts]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase110|<apll>110</apll>]]</td> | <td>[[CombinatorialCase110|<apll>110</apll>]]</td> | ||
<td> [[CombinatorialCase110|<apll> | <td> [[CombinatorialCase110|<apll>M</apll> Permutations of <apll>N</apll> items]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase111|<apll>111</apll>]]</td> | <td>[[CombinatorialCase111|<apll>111</apll>]]</td> | ||
<td> [[CombinatorialCase111|<apll> | <td> [[CombinatorialCase111|<apll>M</apll> Tuples of <apll>N</apll> items]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[CombinatorialCase112|<apll>112</apll>]]</td> | <td>[[CombinatorialCase112|<apll>112</apll>]]</td> | ||
<td> [[CombinatorialCase112|Partitions of the set <apll>{ | <td> [[CombinatorialCase112|Partitions of the set <apll>{⍳M}</apll> into <apll>N</apll> ordered parts]]</td> | ||
</tr> | </tr> | ||
</table> | </table> | ||
Line 73: | Line 73: | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>The second element ('''Count/Generate Flag''') in <apll>a</apll> is an optional | <td>The second element ('''Count/Generate Flag''') in <apll>a</apll> is an optional Integer value where <apll>0</apll> (the default) means '''Count''' the number of elements in the Combinatorial Function as applied to <apll>R</apll> and values greater than <apll>0</apll> mean '''generate''' the array of elements. If the '''Generate Flag''' is <apll>1</apll>, generate the array in an unspecified order. Certain Function Selectors (see the individual Combinatorial Functions) accept values greater than <apll>1</apll>. In particular, the value <apll>2</apll> generates the array in [https://en.wikipedia.org/wiki/Lexicographical_order '''Lexicographic'''] order, and <apll>3</apll> generates the array in [https://en.wikipedia.org/wiki/Gray_code '''Gray Code'''] order.</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
Line 83: | Line 83: | ||
==Introduction== | ==Introduction== | ||
Counting and generating items is fundamental in mathematics, but has been sorely lacking in APL (notwithstanding the counting functions <apll>! | Counting and generating items is fundamental in mathematics, but has been sorely lacking in APL (notwithstanding the counting functions <apll>!N</apll> and <apll>M!N</apll>); instead we have had to rely upon a patchwork of various library routines. | ||
Moreover, most APL papers on the topic have focused on the implementation of the algorithms rather than their organization and syntax mostly because, at the time, there was no unifying concept nor common syntax. | Moreover, most APL papers on the topic have focused on the implementation of the algorithms rather than their organization and syntax mostly because, at the time, there was no unifying concept nor common syntax. | ||
Line 121: | Line 121: | ||
<ul> | <ul> | ||
<li>A function selector of [[CombinatorialCase010|<apll>010</apll>]] means unlabeled balls (0), labeled boxes (1), and at most one ball | <li>A function selector of [[CombinatorialCase010|<apll>010</apll>]] means unlabeled balls (0), labeled boxes (1), and at most one ball per box (0). | ||
If we have 2 unlabeled balls (<span style="font-size: 2em;">●●</span>) and 4 labeled boxes (<apll>1234</apll>) with at most one ball per box, there are <apll>6</apll> (<apll>↔ 2!4</apll>) ways to meet these criteria: | If we have 2 unlabeled balls (<span style="font-size: 2em;">●●</span>) and 4 labeled boxes (<apll>1234</apll>) with at most one ball per box, there are <apll>6</apll> (<apll>↔ 2!4</apll>) ways to meet these criteria: | ||
Line 213: | Line 213: | ||
|} | |} | ||
from which it is easy to see that these criteria correspond to | from which it is easy to see that these criteria correspond to <apll>M</apll> combinations of <apll>N</apll> items (<apll>↔ M!N</apll>). | ||
<div style="background: yellow; padding-left: 1.3em; text-indent: -1.3em;">► Notice how we obtain the generated answer as a <apll>6×M</apll> matrix. In this example, it is obtained by reading the Box Labels in ascending order (<apll>6 M⍴1 2, 1 3, 1 4, 2 3, 2 4, 3 4</apll>). Also note that because each individual Combination is always written in ascending order, that explains why the Function Selector uses '''Unlabeled Balls''' and '''Labeled Boxes''' with '''At Most One Ball per Box'''. That is, the use of Unlabeled Balls forces us to read in ascending order the labels on the Labeled Boxes; because there are <apll>M</apll> Balls and At Most One Ball Per Box we are assured of obtaining exactly <apll>M</apll> box labels per row.</div></li> | |||
<li>A function selector of [[CombinatorialCase110|<apll>110</apll>]] means labeled balls (1), labeled boxes (1), and at most one ball in each box (0). | <li>A function selector of [[CombinatorialCase110|<apll>110</apll>]] means labeled balls (1), labeled boxes (1), and at most one ball in each box (0). | ||
If we have 3 labeled balls (❶❷❸) and 3 labeled boxes (<apll>123</apll>) with at most one ball per box, there are 6 (<apll>↔ | If we have 3 labeled balls (❶❷❸) and 3 labeled boxes (<apll>123</apll>) with at most one ball per box, there are 6 (<apll>↔ !⍠¯3 3 ↔ 3×2×1</apll>) ways to meet these criteria: | ||
{| border="0" cellpadding="5" cellspacing="0" | {| border="0" cellpadding="5" cellspacing="0" | ||
Line 288: | Line 290: | ||
|} | |} | ||
If we have 2 labeled balls (❶❷) and 3 labeled boxes (<apll>123</apll>) with at most one ball per box, there are 6 (<apll>↔ | If we have 2 labeled balls (❶❷) and 3 labeled boxes (<apll>123</apll>) with at most one ball per box, there are 6 (<apll>↔ !⍠¯2 3 ↔ 3×2</apll>) ways to meet these criteria: | ||
{| border="0" cellpadding="5" cellspacing="0" | {| border="0" cellpadding="5" cellspacing="0" | ||
Line 357: | Line 359: | ||
|} | |} | ||
|} | |} | ||
from which it is easy to see that these criteria correspond to <apll>M</apll> permutations of <apll>N</apll> items. When <apll>M=N</apll>, this is the # permutations of <apll>⍳N</apll>, (<apll>↔ !N</apll>), and when <apll>M<N</apll>, this is the # <apll>M</apll>-permutations, also called the [[Variant#Rising_and_Falling_Factorials|Falling Factorial]] (<apll>!⍠((-M) N)</apll>). | |||
<div style="background: yellow; padding-left: 1.3em; text-indent: -1.3em;">► Notice how we obtain the generated answer as a <apll>6×M</apll> matrix differently from the Combinations example. In the two Permutations examples, it is obtained by reading the Box Labels in ascending order of the Ball Labels. For the first Permutations example (with <apll>M=3</apll>), the generated answer is <apll>6 M⍴1 2 3, 2 1 3, 3 1 2, 1 3 2, 2 3 1, 3 2 1</apll>, and for the second one (with <apll>M=2</apll>), it is <apll>6 M⍴1 2, 2 1, 3 1, 1 3, 2 3, 3 2</apll>. It is common to need a different method of generating the answer for many of these Combinatorial Algorithms. | |||
</div> | |||
</li> | </li> | ||
</ul> | </ul> | ||
As a side note, the above examples reveal one of the many insights the Twelvefold Way provides into Combinatorial Algorithms. Previously, you might not have seen any connection between the algorithms for Combinations and Permutations, but, as the above examples show, they are closely related in that they differ only in the use of labeled (1) vs. unlabeled (0) balls; both algorithms use labeled boxes (1) with at most one ball per box (0). | |||
As a side note, the above examples reveal one of the many insights the Twelvefold Way provides into Combinatorial Algorithms. Previously, you might not have seen any connection between the algorithms for Combinations and Permutations, but, as the above examples show, they are closely related in that they differ only in the use of labeled (1) vs. unlabeled (0) balls | |||
==Labeled vs. Unlabeled== | ==Labeled vs. Unlabeled== | ||
Line 454: | Line 459: | ||
===Balls=== | ===Balls=== | ||
In a similar manner, the counts and generations for combinations ([[CombinatorialCase010|<apll>010</apll>]]) and permutations ([[CombinatorialCase110|<apll>110</apll>]]) differ by a factor of <apll>! | In a similar manner, the counts and generations for combinations ([[CombinatorialCase010|<apll>010</apll>]]) and permutations ([[CombinatorialCase110|<apll>110</apll>]]) differ by a factor of <apll>!M</apll>, this time because of the balls: one is unlabeled and the other labeled. That is, the count for <apll>M</apll> combinations of <apll>N</apll> items is | ||
<apll> | <apll>M!N ↔ (!N)÷(!N-M)×!M</apll> | ||
and the count for <apll> | and the count for <apll>M</apll> permutations of <apll>N</apll> items is | ||
<apll>!⍠(- | <apll>!⍠((-M) N) ↔ (!N)÷!N-M</apll> | ||
Of course, when <apll> | Of course, when <apll>M=N</apll>, the permutation count is the familiar <apll>!N</apll>. | ||
==The Functions== | ==The Functions== | ||
Line 468: | Line 473: | ||
The array of functions can be displayed as follows in a table organized by the Function Selector: | The array of functions can be displayed as follows in a table organized by the Function Selector: | ||
{| border="1" cellpadding=" | {| border="1" cellpadding="0" cellspacing="0" | ||
|rowspan="2" align="center"|<big><big>'''FS | |rowspan="2" align="center" style="padding: 1em;" width="20%"|<big><big>'''FS Table'''</big></big> | ||
|colspan="3" align="center" style="border-bottom:1px solid;"|'''Balls Per Box''' | |colspan="3" align="center" style="border-bottom:1px solid; padding: 1em;" width="65%"|'''Balls Per Box''' | ||
|- | |||
| | |||
{| border="0" cellpadding="5" cellspacing="0" width="100%" | |||
|- | |||
|style="text-align: left;"|'''At Most One''' | |||
|style="text-align: right;"|'''xx0''' | |||
|} | |||
| | |||
{| border="0" cellpadding="5" cellspacing="0" width="100%" | |||
|- | |||
|style="text-align: left;"|'''Unrestricted''' | |||
|style="text-align: right;"|'''xx1''' | |||
|} | |||
| | |||
{| border="0" cellpadding="5" cellspacing="0" width="100%" | |||
|- | |||
|style="text-align: left;"|'''At least One''' | |||
|style="text-align: right;"|'''xx2''' | |||
|} | |||
|- | |||
| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" style="padding: 0 5px;" | |||
|- | |||
| style="text-align: left;" |M unlabeled balls | |||
| style="text-align: right;"|'''00x''' | |||
|- | |||
| colspan="2"|N unlabeled boxes | |||
|} | |||
|[[CombinatorialCase000/100| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|M pigeons | |||
|style="text-align: right;"|000 | |||
|- | |||
|colspan="2"|into N holes | |||
|} | |||
]] | |||
|[[CombinatorialCase001| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|partitions of M | |||
|style="text-align: right;"|001 | |||
|- | |||
|colspan="2"|into ≤N parts | |||
|} | |||
]] | |||
|[[CombinatorialCase002| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|partitions of M | |||
|style="text-align: right;"|002 | |||
|- | |||
|colspan="2"|into N parts | |||
|} | |||
]] | |||
|- | |||
| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|M unlabeled balls | |||
|style="text-align: right;"|'''01x''' | |||
|- | |||
|colspan="2"|N labeled boxes | |||
|} | |||
|[[CombinatorialCase010| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|M-combinations | |||
|style="text-align: right;"|010 | |||
|- | |||
|colspan="2"|of N items | |||
|} | |||
]] | |||
|[[CombinatorialCase011| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|M-multisets | |||
|style="text-align: right;"|011 | |||
|- | |||
|colspan="2"|of N items | |||
|} | |||
]] | |||
|[[CombinatorialCase012| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|compositions of M | |||
|style="text-align: right;"|012 | |||
|- | |||
|colspan="2"|into N parts | |||
|} | |||
]] | |||
|- | |||
| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|M labeled balls | |||
|style="text-align: right;"|'''10x''' | |||
|- | |||
|colspan="2"|N unlabeled boxes | |||
|} | |||
|[[CombinatorialCase000/100| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|M pigeons | |||
|style="text-align: right;"|100 | |||
|- | |||
|colspan="2"|into N holes | |||
|} | |||
]] | |||
|[[CombinatorialCase101| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|partitions of <apll>{⍳M}</apll> | |||
|style="text-align: right;"|101 | |||
|- | |||
|colspan="2"|into ≤N parts | |||
|} | |||
]] | |||
|[[CombinatorialCase102| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|partitions of <apll>{⍳M}</apll> | |||
|style="text-align: right;"|102 | |||
|- | |||
|colspan="2"|into N parts | |||
|} | |||
]] | |||
|- | |||
| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|M labeled balls | |||
|style="text-align: right;"|'''11x''' | |||
|- | |||
|colspan="2"|N labeled boxes | |||
|} | |||
|[[CombinatorialCase110| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |||
|style="text-align: left;"|M-permutations | |||
|style="text-align: right;"|110 | |||
|- | |- | ||
| | |colspan="2"|of N items | ||
| | |} | ||
]] | |||
|[[CombinatorialCase111| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |- | ||
| | |style="text-align: left;"|M-tuples | ||
| | |style="text-align: right;"|111 | ||
|- | |- | ||
| | |colspan="2"|of N items | ||
| | |} | ||
| | ]] | ||
|[[CombinatorialCase112| | |||
{| border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="lightblue" style="padding: 0 5px;" | |||
|- | |- | ||
| | |style="text-align: left;"|partitions of <apll>{⍳M}</apll> | ||
|style="text-align: right;"|112 | |||
| | |||
|- | |- | ||
| | |colspan="2"|into N ordered parts | ||
| | |} | ||
]] | |||
|} | |} | ||
Line 503: | Line 647: | ||
<div style="background: yellow; padding-left: 1.3em; text-indent: -1.3em;">► Almost without exception, the counts for these functions grow quite rapidly as the arguments increase (hence the term [https://en.wikipedia.org/wiki/Combinatorial_explosion '''Combinatorial Explosion''']). It's a good idea to check the '''Count''' value before trying to '''Generate''' the corresponding array.</div> | <div style="background: yellow; padding-left: 1.3em; text-indent: -1.3em;">► Almost without exception, the counts for these functions grow quite rapidly as the arguments increase (hence the term [https://en.wikipedia.org/wiki/Combinatorial_explosion '''Combinatorial Explosion''']). It's a good idea to check the '''Count''' value before trying to '''Generate''' the corresponding array.</div> | ||
The expression <apll> | The expression <apll>110‼M N</apll> produces <apll>M</apll> Permutations of <apll>N</apll> items. When <apll>M=N</apll>, it represents the usual Permutation function. | ||
<apll><pre> | <apll><pre> | ||
Line 515: | Line 659: | ||
2 3 1 | 2 3 1 | ||
2 1 3 | 2 1 3 | ||
110 2‼3 ⍝ Generate the !3 Permutations in Lexicographic order | |||
1 2 3 | |||
1 3 2 | |||
2 1 3 | |||
2 3 1 | |||
3 1 2 | |||
3 2 1 | |||
110 1‼2 3 ⍝ Generate the 2 Permutations of 3 items | 110 1‼2 3 ⍝ Generate the 2 Permutations of 3 items | ||
1 2 | 1 2 | ||
Line 532: | Line 683: | ||
</pre></apll> | </pre></apll> | ||
The expression <apll> | The expression <apll>10‼M N</apll> produces <apll>M</apll> Combinations of <apll>N</apll> items. | ||
<apll><pre> | <apll><pre> | ||
Line 545: | Line 696: | ||
2 3 5 | 2 3 5 | ||
1 4 5 | 1 4 5 | ||
2 4 5 | |||
3 4 5 | |||
10 2‼3 5 ⍝ Combinations in Lexicographic order | |||
1 2 3 | |||
1 2 4 | |||
1 2 5 | |||
1 3 4 | |||
1 3 5 | |||
1 4 5 | |||
2 3 4 | |||
2 3 5 | |||
2 4 5 | 2 4 5 | ||
3 4 5 | 3 4 5 | ||
</pre></apll> | </pre></apll> | ||
The expression <apll> | The expression <apll>1‼M N</apll> produces Partitions of <apll>M</apll> into at most <apll>N</apll> parts (as a nested array). | ||
<apll><pre> | <apll><pre> | ||
Line 574: | Line 736: | ||
==Special Cases== | ==Special Cases== | ||
The problem of counting [[CombinatorialCase001|Partitions of <apll> | The problem of counting [[CombinatorialCase001|Partitions of <apll>M</apll> Into At Most <apll>M</apll> Parts]] has been well studied. As alluded to above, the brilliantly intuitive Indian mathematician [https://en.wikipedia.org/wiki/Srinivasa_Ramanujan Srinivasa Ramanujan] along with [https://en.wikipedia.org/wiki/G._H._Hardy G. H. Hardy] and [https://en.wikipedia.org/wiki/Hans_Rademacher Hans Rademacher] have provided a convergent series which can produce an exact value for this number. Fortunately, this algorithm (referred to as [https://en.wikipedia.org/wiki/Partition_(number_theory)#Approximation_formulas Hardy-Ramanujan-Rademacher] or HRR) has been coded by Frederik Johansson<ref name="Johansson">Johansson, F. (2012). Efficient implementation of the Hardy–Ramanujan–Rademacher formula. LMS Journal of Computation and Mathematics, 15, 341-359. doi:10.1112/S1461157012001088</ref> using the open source library [http://mpfr.org MPFR] (Multiple Precision Floating Point) and is available in the open source library [http://flintlib.org FLINT] (Fast Library for Number Theory). This code is used for the function <apll>1‼M</apll> when <apll>M</apll> is in the range <apll>395 < M < 2<sup>31</sup></apll> on the 32-bit version of NARS2000, and in the range <apll>395 < M < 2<sup>63</sup></apll> on the 64-bit version. The code is so efficient that it can calculate <apll>1‼1E12</apll> in under a minute (on a 64-bit machine only!). However, if you try this, be sure to assign the result to a variable as otherwise it will display a number with <apll>1,113,996</apll> digits! | ||
Correspondingly, because partitions of <apll> | Correspondingly, because partitions of <apll>M</apll> into <apll>N</apll> non-negative parts ([[CombinatorialCase001|<apll>001</apll>]]) is the same as partitions of <apll>N+M</apll> into <apll>N</apll> positive parts ([[CombinatorialCase002|<apll>002</apll>]]), (i.e., <apll>1‼M N ←→ 2‼(M+N) N</apll><ref>https://en.wikipedia.org/wiki/Twelvefold_way#case_fnx</ref>), the case of <apll>2‼(2×M) M</apll> also takes advantage of the fast algorithm. | ||
==Memoization== | ==Memoization== | ||
Line 582: | Line 744: | ||
This [https://en.wikipedia.org/wiki/Memoization technique] is a form of caching used to speed up certain algorithms, particularly recursive ones. | This [https://en.wikipedia.org/wiki/Memoization technique] is a form of caching used to speed up certain algorithms, particularly recursive ones. | ||
Two of the Combinatorial Functions ([[CombinatorialCase001|<apll>001</apll>]] and [[CombinatorialCase002|<apll>002</apll>]]) are dependent on the following recurrence relation for [https://en.wikipedia.org/wiki/Partition_(number_theory) Partition Numbers] defined on <apll> | Two of the Combinatorial Functions ([[CombinatorialCase001|<apll>001</apll>]] and [[CombinatorialCase002|<apll>002</apll>]]) are dependent on the following recurrence relation for [https://en.wikipedia.org/wiki/Partition_(number_theory) Partition Numbers] defined on integer <apll>n</apll> and <apll>k</apll>: | ||
<apll><pre>P(0,0) = 1 | <apll><pre>P(0,0) = 1 | ||
P(n,k) = 0 for n≤0 or k≤0 | |||
P(n,k) = P(n-k,k) + P(n-1,k-1)</pre></apll> | P(n,k) = P(n-k,k) + P(n-1,k-1)</pre></apll> | ||
Within a session of the interpreter, these values are cached internally so that subsequent requests for already calculated Partition Numbers are sped up significantly. | Within a session of the interpreter, these values are cached internally so that subsequent requests for already calculated Partition Numbers are sped up significantly. | ||
Three other Combinatorial Functions ([[CombinatorialCase101|<apll>101</apll>]], [[CombinatorialCase102|<apll>102</apll>]], and [[CombinatorialCase112|<apll>112</apll>]]) are dependent on the [https://en.wikipedia.org/wiki/Stirling_numbers_of_the_second_kind Stirling numbers of the 2nd kind]. They satisfy the following recurrence relation defined on <apll>n≥0</apll> and <apll>k≥0</apll>: | Three other Combinatorial Functions ([[CombinatorialCase101|<apll>101</apll>]], [[CombinatorialCase102|<apll>102</apll>]], and [[CombinatorialCase112|<apll>112</apll>]]) are dependent on the [https://en.wikipedia.org/wiki/Stirling_numbers_of_the_second_kind Stirling numbers of the 2nd kind]. They satisfy the following recurrence relation defined on integer <apll>n≥0</apll> and <apll>k≥0</apll>: | ||
<apll><pre> | <apll><pre> | ||
S(0,0) = 1 | S(0,0) = 1 | ||
S(0,n) = S(n,0) = 0 for n>0 | |||
S(n,k) = k × S(n-1,k) + S(n-1,k-1)</pre></apll> | S(n,k) = k × S(n-1,k) + S(n-1,k-1)</pre></apll> | ||
Line 617: | Line 781: | ||
* This organizational framework is ideally suited for implementation in APL for both counting and generation by referencing the individual algorithms using a function selector as the (left) operand to a new monadic primitive operator. | * This organizational framework is ideally suited for implementation in APL for both counting and generation by referencing the individual algorithms using a function selector as the (left) operand to a new monadic primitive operator. | ||
* Insight into these Combinatorial Algorithms is gained when viewed from the perspective of the Twelvefold Way. To wit: | * Insight into these Combinatorial Algorithms is gained when viewed from the perspective of the Twelvefold Way. To wit: | ||
** The relationships among the algorithms is made clearer when comparing their APL versions, especially through identities. | ** The relationships among the algorithms is made clearer when comparing their APL versions, especially through identities<ref name="Smith" />. | ||
** The algorithms are shown to have considerable dependence amongst themselves as shown through APL identities. | ** The algorithms are shown to have considerable dependence amongst themselves as shown through APL identities<ref name="Smith" />. | ||
** Interesting similarities within the function selector table are identified and are worthy of further investigation. | ** Interesting similarities within the function selector table are identified and are worthy of further investigation<ref name="Smith" />. | ||
* Thanks to the work of D. E. Knuth in his TAoCP Vol 4A, each of the twelve ways has a high quality algorithm behind it. | * Thanks to the work of D. E. Knuth in his TAoCP Vol 4A, each of the twelve ways has a high quality algorithm behind it<ref name="Smith" />. | ||
* Finally, APL programmers need no longer search for the fastest APL program to generate any of several Combinatorial Counts or Generations as the fastest way is now available primitively. | * Finally, APL programmers need no longer search for the fastest APL program to generate any of several Combinatorial Counts or Generations as the fastest way is now available primitively. | ||
Latest revision as of 09:27, 15 September 2023
|
||||
R is a non-negative numeric scalar or one- or two-element vector. If R has only one element, it is treated as if the value were duplicated as in 2⍴R. For convenience in the description below, the two elements are referred to as M and N as in (M N)←2⍴R. | ||||
a is a non-negative numeric scalar or one- or two- element vector which serves as the Selector for the Twelve Combinatorial Functions.
The first element (Function Selector) in a is a non-negative integer for each of the Twelve functions (where the number is written here as a decimal number with three digits to emphasize that each digit has a separate meaning): |
||||
The second element (Count/Generate Flag) in a is an optional Integer value where 0 (the default) means Count the number of elements in the Combinatorial Function as applied to R and values greater than 0 mean generate the array of elements. If the Generate Flag is 1, generate the array in an unspecified order. Certain Function Selectors (see the individual Combinatorial Functions) accept values greater than 1. In particular, the value 2 generates the array in Lexicographic order, and 3 generates the array in Gray Code order. | ||||
The symbol ‼ (U+203C) can be entered from the default keyboard layout with Alt-’k’ or Ctrl-’k’, depending upon your choice of keyboard layouts. |
Introduction
Counting and generating items is fundamental in mathematics, but has been sorely lacking in APL (notwithstanding the counting functions !N and M!N); instead we have had to rely upon a patchwork of various library routines.
Moreover, most APL papers on the topic have focused on the implementation of the algorithms rather than their organization and syntax mostly because, at the time, there was no unifying concept nor common syntax.
The main purpose of this document is to present in APL a unified organizing principle to classify and access various Combinatorial Algorithms.
A secondary purpose is to shed light on the relationships between the various algorithms through a new perspective provided by Gian-Carlo Rota[1]’s clever way to fit them into a single organizational framework.
The goal of this document is to describe a single APL primitive to both Count and Generate various Combinatorial Arrays: permutations, combinations, compositions, partitions, etc. The unifying (and very APL-like) principle for such a primitive is Gian-Carlo Rota's Twelvefold Way[2] as described in Richard Stanley's "Enumerative Combinatorics"[3] and Knuth’s TAoCP, Vol. 4A[4] among other references.
The Twelvefold Way
This elegant notion consolidates twelve Combinatorial Algorithms into a single 2×2×3 array based on the simple concept of placing balls into boxes (urns, to you old-timers). The three dimensions of the array can be described as follows:
- The Balls may be labeled (or not) {2 ways},
- The Boxes may be labeled (or not) {2 ways}, and
- The Capacity of Balls in a Box may be one of (at most one | unrestricted | at least one) {3 ways}.
Amazingly, these twelve choices spanning three dimensions knit together within a single concept (balls in boxes) all of the following interesting, fundamental, and previously disparate and disorganized Combinatorial Algorithms:
- Permutations
- Combinations
- Compositions
- Multisets
- Partitions of a set
- Partitions of a number
- Tuples
- Pigeon Holes
As mentioned above, although the first element of the Function Selector is an integer, it is written here with three digits to emphasize that each digit has a separate meaning. Those meanings are exactly related to the 2×2×3 array mentioned above.
- The first digit represents the Balls as Unlabeled (0) or Labeled (1)
- The second digit represents the Boxes as Unlabeled (0) or Labeled (1)
- The third digit represents the Capacity of Balls in a Box as one of At most One (0), Unrestricted (1), or At Least One (2).
For example:
- A function selector of 010 means unlabeled balls (0), labeled boxes (1), and at most one ball per box (0).
If we have 2 unlabeled balls (●●) and 4 labeled boxes (1234) with at most one ball per box, there are 6 (↔ 2!4) ways to meet these criteria:
● ● 1 2 3 4 ● ● 1 2 3 4 ● ● 1 2 3 4 ● ● 1 2 3 4 ● ● 1 2 3 4 ● ● 1 2 3 4 ⇐ Box Contents ⇐ Box Labels (blank = Unlabeled) from which it is easy to see that these criteria correspond to M combinations of N items (↔ M!N).
► Notice how we obtain the generated answer as a 6×M matrix. In this example, it is obtained by reading the Box Labels in ascending order (6 M⍴1 2, 1 3, 1 4, 2 3, 2 4, 3 4). Also note that because each individual Combination is always written in ascending order, that explains why the Function Selector uses Unlabeled Balls and Labeled Boxes with At Most One Ball per Box. That is, the use of Unlabeled Balls forces us to read in ascending order the labels on the Labeled Boxes; because there are M Balls and At Most One Ball Per Box we are assured of obtaining exactly M box labels per row. - A function selector of 110 means labeled balls (1), labeled boxes (1), and at most one ball in each box (0).
If we have 3 labeled balls (❶❷❸) and 3 labeled boxes (123) with at most one ball per box, there are 6 (↔ !⍠¯3 3 ↔ 3×2×1) ways to meet these criteria:
❶ ❷ ❸ 1 2 3 ❷ ❶ ❸ 1 2 3 ❷ ❸ ❶ 1 2 3 ❶ ❸ ❷ 1 2 3 ❸ ❶ ❷ 1 2 3 ❸ ❷ ❶ 1 2 3 If we have 2 labeled balls (❶❷) and 3 labeled boxes (123) with at most one ball per box, there are 6 (↔ !⍠¯2 3 ↔ 3×2) ways to meet these criteria:
❶ ❷ 1 2 3 ❷ ❶ 1 2 3 ❷ ❶ 1 2 3 ❶ ❷ 1 2 3 ❶ ❷ 1 2 3 ❷ ❶ 1 2 3 from which it is easy to see that these criteria correspond to M permutations of N items. When M=N, this is the # permutations of ⍳N, (↔ !N), and when M<N, this is the # M-permutations, also called the Falling Factorial (!⍠((-M) N)).
► Notice how we obtain the generated answer as a 6×M matrix differently from the Combinations example. In the two Permutations examples, it is obtained by reading the Box Labels in ascending order of the Ball Labels. For the first Permutations example (with M=3), the generated answer is 6 M⍴1 2 3, 2 1 3, 3 1 2, 1 3 2, 2 3 1, 3 2 1, and for the second one (with M=2), it is 6 M⍴1 2, 2 1, 3 1, 1 3, 2 3, 3 2. It is common to need a different method of generating the answer for many of these Combinatorial Algorithms.
As a side note, the above examples reveal one of the many insights the Twelvefold Way provides into Combinatorial Algorithms. Previously, you might not have seen any connection between the algorithms for Combinations and Permutations, but, as the above examples show, they are closely related in that they differ only in the use of labeled (1) vs. unlabeled (0) balls; both algorithms use labeled boxes (1) with at most one ball per box (0).
Labeled vs. Unlabeled
Boxes
For most cases, the boxes are the columns of the result. Two or more labeled boxes may hold identical content, but because the boxes are labeled, they are considered distinct. On the other hand, unlabeled boxes with identical content are indistinguishable. For example, the following (partial) configurations of 3 unlabeled balls (●●●) in 3 unlabeled boxes
|
|
|
are all considered equivalent and are counted only once because the boxes are unlabeled.
Similarly, the following (partial) configurations of 3 labeled balls (❶❷❸) in 2 unlabeled boxes
|
|
|
|
are also all considered equivalent and are counted only once, again because the boxes are unlabeled.
Note that the order of the (labeled) balls within a box is ignored which means that even if the boxes were labeled, the first and third configurations above are equivalent, as are the second and fourth.
Balls
In a similar manner, the counts and generations for combinations (010) and permutations (110) differ by a factor of !M, this time because of the balls: one is unlabeled and the other labeled. That is, the count for M combinations of N items is
M!N ↔ (!N)÷(!N-M)×!M
and the count for M permutations of N items is
!⍠((-M) N) ↔ (!N)÷!N-M
Of course, when M=N, the permutation count is the familiar !N.
The Functions
The array of functions can be displayed as follows in a table organized by the Function Selector:
Click on one of the above colored cells to see more detail on that function.
Examples
The expression 110‼M N produces M Permutations of N items. When M=N, it represents the usual Permutation function.
110‼3 ⍝ Count the !3 Permutations 6 110 1‼3 ⍝ Generate the !3 Permutations 1 2 3 1 3 2 3 1 2 3 2 1 2 3 1 2 1 3 110 2‼3 ⍝ Generate the !3 Permutations in Lexicographic order 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 110 1‼2 3 ⍝ Generate the 2 Permutations of 3 items 1 2 2 1 1 3 3 1 2 3 3 2 perm←{110 1‼⍵} ⍝ Permutation function perm 3 1 2 3 1 3 2 3 1 2 3 2 1 2 3 1 2 1 3
The expression 10‼M N produces M Combinations of N items.
comb←{10 1‼⍺ ⍵} ⍝ Combinations function 3 comb 5 1 2 3 1 2 4 1 3 4 2 3 4 1 2 5 1 3 5 2 3 5 1 4 5 2 4 5 3 4 5 10 2‼3 5 ⍝ Combinations in Lexicographic order 1 2 3 1 2 4 1 2 5 1 3 4 1 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5
The expression 1‼M N produces Partitions of M into at most N parts (as a nested array).
1‼7 3 8 ⍪1 1‼7 3 7 6 1 5 2 5 1 1 4 3 4 2 1 3 3 1 3 2 2
If you have seen the movie “The Man Who Knew Infinity” (2015) (about the life and academic career of the brilliant Indian mathematician Srinivasa Ramanujan), you may recall that at one point it focuses on the problem of calculating p(200) — the number of Partitions of the number 200 into at most 200 parts. This number can be calculated by
1‼200 3972999029388
in a few hundred-thousandths of a second.
Special Cases
The problem of counting Partitions of M Into At Most M Parts has been well studied. As alluded to above, the brilliantly intuitive Indian mathematician Srinivasa Ramanujan along with G. H. Hardy and Hans Rademacher have provided a convergent series which can produce an exact value for this number. Fortunately, this algorithm (referred to as Hardy-Ramanujan-Rademacher or HRR) has been coded by Frederik Johansson[5] using the open source library MPFR (Multiple Precision Floating Point) and is available in the open source library FLINT (Fast Library for Number Theory). This code is used for the function 1‼M when M is in the range 395 < M < 231 on the 32-bit version of NARS2000, and in the range 395 < M < 263 on the 64-bit version. The code is so efficient that it can calculate 1‼1E12 in under a minute (on a 64-bit machine only!). However, if you try this, be sure to assign the result to a variable as otherwise it will display a number with 1,113,996 digits!
Correspondingly, because partitions of M into N non-negative parts (001) is the same as partitions of N+M into N positive parts (002), (i.e., 1‼M N ←→ 2‼(M+N) N[6]), the case of 2‼(2×M) M also takes advantage of the fast algorithm.
Memoization
This technique is a form of caching used to speed up certain algorithms, particularly recursive ones.
Two of the Combinatorial Functions (001 and 002) are dependent on the following recurrence relation for Partition Numbers defined on integer n and k:
P(0,0) = 1 P(n,k) = 0 for n≤0 or k≤0 P(n,k) = P(n-k,k) + P(n-1,k-1)
Within a session of the interpreter, these values are cached internally so that subsequent requests for already calculated Partition Numbers are sped up significantly.
Three other Combinatorial Functions (101, 102, and 112) are dependent on the Stirling numbers of the 2nd kind. They satisfy the following recurrence relation defined on integer n≥0 and k≥0:
S(0,0) = 1 S(0,n) = S(n,0) = 0 for n>0 S(n,k) = k × S(n-1,k) + S(n-1,k-1)
These numbers are also cached internally by the interpreter so as to speed up subsequent access.
In case you need to clear the cache so as to time the internal algorithms starting with an empty cache, use the expression
∘‼1 Cache cleared
Note that this expression does not clear certain caching internal to the above HRR algorithm in FLINT.
History
The idea of consolidating these twelve algorithms into a single primitive is credited to Gian-Carlo Rota through a series of lectures given at the Massachusetts Institute of Technology (MIT). The mathematics behind the Twelvefold Way is described in several places, most notably in Richard Stanley's Enumerative Combinatorics[3], and Wikipedia[2]. The name was suggested by Joel Spencer[7].
Implementation
This Combinatorial Operator is implemented in the Released version of NARS2000 and may be downloaded from here. For an in-depth look at the Twelvefold Way and its implementation in APL, see Smith's[8] paper.
Conclusions
- Rota’s amazing Twelvefold Way of consolidating numerous Combinatorial Algorithms through the unifying concept of Balls in Boxes into a single organizational framework is presented and each algorithm is discussed in detail with examples.
- This organizational framework is ideally suited for implementation in APL for both counting and generation by referencing the individual algorithms using a function selector as the (left) operand to a new monadic primitive operator.
- Insight into these Combinatorial Algorithms is gained when viewed from the perspective of the Twelvefold Way. To wit:
- The relationships among the algorithms is made clearer when comparing their APL versions, especially through identities[8].
- The algorithms are shown to have considerable dependence amongst themselves as shown through APL identities[8].
- Interesting similarities within the function selector table are identified and are worthy of further investigation[8].
- Thanks to the work of D. E. Knuth in his TAoCP Vol 4A, each of the twelve ways has a high quality algorithm behind it[8].
- Finally, APL programmers need no longer search for the fastest APL program to generate any of several Combinatorial Counts or Generations as the fastest way is now available primitively.
Acknowledgments
No paper is written in isolation, and this paper is no exception. I’d like to thank David Liebtag, Roy Sykes, Norman Thomson, Jim Brown, Roger Hui, and Michael Turniansky for their helpful advice and suggestions.
References
- ↑ Rota, Gian-Carlo [1]
- ↑ 2.0 2.1 Wikipedia "Twelvefold Way"
- ↑ 3.0 3.1 Stanley, Richard P. (1997, 1999). Enumerative Combinatorics, Volumes 1 and 2. Cambridge University Press. ISBN 0-521-55309-1, ISBN 0-521-56069-1
- ↑ Knuth, Donald E., “The Art of Computer Programming”, Volume 4A, Combinatorial Algorithms, p. 390, Addison Wesley, ISBN 0-201-89685-0
- ↑ Johansson, F. (2012). Efficient implementation of the Hardy–Ramanujan–Rademacher formula. LMS Journal of Computation and Mathematics, 15, 341-359. doi:10.1112/S1461157012001088
- ↑ https://en.wikipedia.org/wiki/Twelvefold_way#case_fnx
- ↑ Joel Spencer
- ↑ 8.0 8.1 8.2 8.3 8.4 Smith, Bob "A Combinatorial Operator", 2016-2017