1502.06062v1-Multi-level Loop-less Algorithm for Multi-set Permutations
1502.06062v1-Multi-level Loop-less Algorithm for Multi-set Permutations
Tadao Takaoka
Department of Computer Science, University of Canterbury
Christchurch, New Zealand
Email: [email protected]
Abstract: We present an algorithm that generates multiset permutations in O(1) time for each
permutation, that is, by a loop-less algorithm with O(n) extra memory requirement. There already
exist several such algorithms that generate multiset permutations in various orders. For multiset
permutations, we combine two loop-less algorithms that are designed in the same principle of tree
traversal. Our order of generation is different from any existing order, and the algorithm is
simpler and faster than the previous ones. We also apply the new algorithm to parking functions.
1. Introduction.
O(1) time generation for combinatorial objects, such as permutations and combinations, is known
also as loop-less algorithms. That is, given the current object, we generate the next object loop-
lessly, e.g., with a finite number of statements. This is also called combinatorial Gray code, since
the idea is a generalization of binary Gray code to more general combinatorial objects. Ehrich [5]
was the first to investigate this topic. Since then many loop-less algorithms were invented for
various objects, such as permutations, combinations, parenthesis strings, etc. To list up just a few,
see Bitner, et al. [1], Ehrlich [5], Lehmer [12], Eades and McKay [4], Chase [3] for combinations,
Johnson [7] and Heap [6] for permutations, Mikawa and Takaoka [13], Vajnovszki [19], and
Walsh [21] for parenthesis strings. We focus on the recent topic of multiset permutations in this
paper.
There have been several algorithms that generate multiset permutations in O(1) time per
permutation. Korsh and Lipschutz (1997) [10] was the first to investigate this problem. This
algorithm achieves O(1) time, but uses a linked list for a container of permutations. Thus we can
go from (1, ..., 1, 2, ..., 2) to (2, ..., 2, 1, ..., 1) in O(1) time by pointer manipulations. Since this
paper, there was a question whether we can do the work with only arrays. There were two
solutions by Takaoka [17] and Vajnovzski [20]. Both require O(kn) space apart from the main
container of permutations, where k is the number of distinct items. Thus we have a question
whether we can do the work with only O(n) extra space. Again we have two solutions by Korsh
and LaFollette (2004) [11] and Takaoka and Violich (2005) [18]. Those algorithms are based on
the two-level approach, which we use in this paper also. In [11], The upper structure is based on
Johnson-Trotter algorithm and the lower structure is based on Eades and McKay [4] for
combination generation. The upper structure for the latter method [18] is the same, Johnson-
Trotter, and the lower is based on Chase’s hard algorithm [3] for combination generation.
Suppose we have a multi-set (1,…,1,2,…,2,……k,…,k), where there are n[i] items of i, for
i=1, …, k. The upper algorithm controls which item i to move, and the lower algorithm controls
the movement of those n[i] identical items by regarding them as ones and others as zeros.
In this paper, we use Johnson-Trotter for the upper structure and Takaoka’s algorithm [16] for
combination generation for the lower structure, which is less restrictive than Eades-McKay and
Chase. Both the upper structure and lower structure are based on the idea of tree traversal. Thus
our algorithm has a more transparent and consistent design methodology and the program code is
shorter than existing ones. To make the problem more visible we start from an example. The
following is the list of permutations of (1, 2, 3, 4) by Johnson-Trotter, which should be read
column-wise.
In this list 1 moves right over (2, 3, 4). Then 2 moves right. Then 1 moves left over (3, 2, 4), etc.,
alternately. If we remove 1 at the top of each column, we have permutations ( 2, 3, 4), (3, 2, 4), (3,
4, 2), (4, 3, 2), (4, 2, 3), (2, 4, 3). This list of permutations has a similar pattern of the movement
of 2 going back and forth alternately. Later we show an O(1) time algorithm for Johnson-Trotter.
The next list is the list of combinations of four items, called 4-combinations out of 6 items {1,
2, 3, 4, 5, 6} in in-place expression and binary vector form. The binary vector is for illustration
purposes. In later sections, we generate only in-place forms. In combination generation, we
generate combinations from ones at the left end to the right end, which we call the forward
generation. When we use combination generation repeatedly as the lower structure of multiset
permutation generation, we use forward generation and backward generation alternately.
Backward generation is to generate combinations in reverse order of forward generation. We can
repeat forward and backward with O(1) time re-initialization. We call this property “reversible”.
If we can repeat forward only with O(1) re-initialization, it is called repeatable.
We note that there is only one change from combination to combination in the in-place list, and in
the binary vector list, a one moves over a block of ones, and does not change the relative order of
zeroes. From 011110 to 011011, for example, the 4th element and 6th element are swapped, e. g.,
the 4th element, 1, goes over the 5th element, 1. In general, the 1 may go over several consecutive
1’s in a larger example. Note also that four ones at the left end finally come to the right end of the
binary vector, and vice versa for backward generation. We can modify the algorithm so that
forward generation and backward generation alternate to fit into Johnson-Trotter.
Suppose we have a list of multiset, such as (1, 1, 2, 3, 3, 4, 4, 4), we move 1’s to the right
using Takaoka’s algorithm by regarding them as ones and other items as zeroes. When all 1’s
arrive at the right end, that is, (2, 3, 3, 4, 4, 4, 1, 1), we perform one step of the movement of 2’s
to the right, and 1’s start to move to the left, resulting in (1, 1, 3, 2, 3, 4, 4, 4). That is, the
movement of each item in Johnson-Trotter is generalized by the movements of several identical
items by a combination generation algorithm. In Korsh and LaFollette [11], the combination
algorithm is that of Eades and McKay [4], and in Takaoka and Violich [18], it was Chase’s [3]
algorithm.
The following is the complete list of permutations of the multiset (1, 1, 2, 2, 3) by our algorithm.
Algorithm 1. Iterative tree traversal in pseudo code. i keeps track of the current level of the tree.
vi current path
w object a
next object a’
next vi opposite path
When we come to the last child of a parent (w in the above figure), we have to update up[i] to
up[i-1] so that when we visit the last leaf of the sub-tree rooted at w, we can come back directly
from the leaf to w or its ancestor if w itself is a last child. We refer to the paths from vi to a and
from next vi to a’ as the current path and the opposite path. A current path and opposite path
consist of last children and first children respectively except for the left ends. If next vi in the
above figure is a last child, we further set vi to the next node of next vi, say u, so we can avoid
O(n) time to set up the environment for such u’s later. This is illustrated in the above figure by the
path from “next vi” to “next object a’ ”. That is, when we traverse down the current path, we
prepare for the opposite path so that we can jump over the opposite path from level i to the leaf.
In this situation a and a’ share the same prefix from position 1 to i-1. We call i the difference
point. The two strings can also share the same suffix (empty in the above figure). Let the longest
such is from position j+1 to n. Then we call j the solution point. Intuitively this is the point where
the difference caused upstream is solved.
3. Review of Johnson-Trotter
In the iterative algorithm for Johnson-Trotter, the level of the tree corresponds to the item we are
trying to move. Parity corresponds to the direction of movement, left or right, represented by -1
(odd parity) or +1 (even parity). Array “p” is to hold the position of item x in array “a”. Array
element “c[i]” is to count the number of child nodes at level i and check the last child condition.
Procedure “move(x)” is to move item x to the direction given by d[x], and update the positions of
affected items. The comment /*output */ shows the point where a new permutation is generated.
Array “d” plays the role of parity in Algorithm 1. In the program, level variable i corresponds to
item i. If we regard the position of each item as the label of the node, we have a close
correspondence with the twisted lexico-tree.
In the following, the work “maintain combinations” includes “return the next combination”.
Lower Level
Combination-server /* This server performs the following operations on demand */
begin
Initialize n[1]-combinations out of limit[1] items
Initialize n[2]-combinations out of limit[2] items
…
Initialize n[k-1]-combinations out of limit[k-1] items
Upper Level
procedure move(x);
begin var w;
c[i]:=c[i]+1;
Let the change of combination be from “from” to “to”;
if d[i-1]>0 then from=from + base[i]; to:=to + base[i];
w:=a[from]; a[from]:=a[to]; a[to]:=w; /* output */
end;
begin {main program}
for i:=1 to k-1 do call combination-server to
initialize n[i]-combinations out of limit[i] items;
for i:=1 to k do begin
a[i]:=i, up[i]:=i; p[i]:=i; c[i]:=1; d[i]:=1;
end;
repeat
i:=up[1]; up[1]:=1;
call combination-server for next combination for item i;
if i<k then move(i);
if c[i] = C(n[i], R) then begin /* checking a last child */
Re-initialize combinations of item i for reverse direction;
up[i]:=up[i+1]; up[i]:=i+1;
c[i]:=1;
d[i]:= d[i];
end
until i=k;
end.
Re-initialization of combinations of item i must be done in O(1) time. This will be mentioned in
Appendix 1. The value of C(n[i], R) is potentially large, and may not be contained in a single
precision variable. We can use the termination condition (i=0) in the Algorithm 4 in Appendix 1,
and bring the effect to the calling site. As initialization for combinations of item i takes O(n[i])
time, Algorithm 3 takes O(R) time at the beginning. As the size of an object of n[i]-combinations
is O(n[i]), the total space requirement is O(R). In the Appendix 2 we declare fixed-sized arrays
for readability. It is straightforward to allocate necessary space dynamically using the “calloc”
function in C.
Takaoka’s algorithm for combination generation is designed by tree traversal, and has the
following desirable property that fits into the upper algorithm of Johnson-Trotter as the following
fact shows. The details are given in Appendices.
Fact. Takaoka’s algorithm generates combinations of k items out of n items in O(1) time per
combination in O(k) space. In the hypothetical binary vector of size n for a combination, the
movement of 1 goes over zero or more consecutive 1s. The relative order of 0s is not changed. In
the binary vector, 1s start from the left end and end at the right end. If we call the range [1, ..,
limit[i]] the capsule of items i, the relative order of items i in the capsule is not changed by the
movement of lower items. With O(1) time re-initialization, the algorithm continues to generate
the same set of combinations in reverse order.
Example. Let n=3. (1,2,2) is a solution. Car 1 is given lot 1. Cars 2 and 3 are given lot 2. If car 2
or 3 finds lot 2 is occupied, it can go to lot 3. As the assignment can permute, (2,1,2) is also a
solution. This is a multiset permutation. How about (1,3,3)? If car 2 occupies lot 3, car 3 gets
trouble, and vice versa.
Let us call a solution standard if p(1) ≦…≦p(n) is satisfied. The standard solutions are the
Catalan sequences, which further satisfy p(i) ≦i. In [16], we have an O(1) time algorithm for the
Catalan sequences, which we call “Catalan”. Here we propose to use Catalan as the upper
algorithm and the multiset permutation generation algorithm, called “Multiperm”, in the previous
section as the lower structure. The combination mechanism for those two is of type 1 in Section 2,
resulting in three levels in total. How to combine those two needs some care.
If not impossible, Multiperm seems to be difficult to run repeatedly or reversely with O(1) re-
initialization. Thus we propose to use what is called “time stealing” in [18]. This is to prepare two
identical copies of Multiperm together with data structures, named Multiperm1 and Multiperm2.
As the size of the set of multiset permutations is greater than the size of the parking function, n, it
is easy to re-initialize Multiperm2 while Multiperm1 is running, and vice versa, within the
constraint of O(1) time per parking function.
Thus we claim that we can generate parking functions in O(1) time with O(n) space.
6. Concluding Remarks
The cpu times in seconds of the three algorithms are given in the following table.
n[1], …, n[5] Korsh & LaFollette Takaoka & Violich This paper
3 3 3 3 3 52.1 28.2 26.2
We showed how to design a loop-less algorithm for multiset permutation generation based on a
two-level approach. To avoid complications, we used an abstract algorithm, called “combination
server”, which delivers combinations of various sizes one by one at the request of the upper
algorithm of Johnson-Trotter. If we implement our algorithm completely in a procedural language,
we can use two-dimensional arrays, another dimension corresponding to which kind of items are
moving. The source code in Appendix 2 is based on this approach by two dimensional arrays.
Note that with this approach, memory requirement can be O(R), where R is the total size of each
permutation. Precisely speaking, we would need to maintain eight arrays of size n[i] for each item
i. Those arrays need to be maintained by pointers for dynamic memory allocation, although the
main container of permutations remains to be a fixed array of size R. In the upper structure of the
algorithm we need seven arrays of size k where k is the number of distinct items. In [11], 20
arrays of various sizes are used and in [18] an array of size R and nine arrays of size k are used. It
remains to be seen whether we can further simplify the algorithm or reduce memory requirement.
Note that the repeatability or reversibility of the lower level combinatorial generation with O(1)
re-initialization is essential. We used the latter for our multiset permutation generation. It is
interesting to see if the former will work in our case. It works for parenthesis strings as shown in
Takaoka and Violich [18 ].
References
[1] Bitner, J.R., G. Ehrlich and E.M. Reingold, “Efficient Generation of Binary Reflected Gray
Code and its Applications,” CACM 19 (1976) pp. 517-521.
[2] Canfield, E. R., and S. G. Williamson, A loop-free algorithm for generating the
linear extensions of a poset, Order 12 (1995) 57-75
[3] Chase, P. J., Combinatorial generation and graylex ordering, Congressus Numerantium, 69
(1989) 215-242
[4] Eades, P and McKay, B, Al algorithm for generating subsets of fixed size with a strong
minimal change property, Info. Proc. Lett., 19 (1984) 131-133.
[5] Ehrlich, G., Loopless algorithms for generating permutations, combinations, and other
combinatorial configurations, JACM, 20 (1973) 500-513.
[6] Heap, B.R., “Permutations by Interchanges,” Computer Journal 6 (1963) pp. 293-294
[7] Johnson, S.M., “Generation of Permutations by Transpositions,” Math. Comp. 15(1963)282-
285.
[8] Joichi, J.T., D.E. White, and S.G. Williamson, Combinatorial Gray codes, SIAM Jour.
Comput., 9 (1980) 130-141.
[9] Konheim, A. G. and B. Weiss, An occupancy discipline and applications, SIAM Jour. On
Applied Math., 14 (1966) 1266-1274
[10] Korsh, J and S. Lipschutz, Generating Multiset Permutations in Constant Time,” Jour.
Algorithms, 25 (1997) pp. 321-335.
[11] Korsh, J and LaFollette, P. S., Loopless array generation of multiset permutations, The
Computer Journal 47 (5) (2004)612-621
[12] Lehmer, D.H., “The Machine Tools of Combinatorics,” in Applied Combinatorial
Mathematics (E.F. Beckenbach Ed.), Wiley, New York (1964) pp. 5-31.
[13] Mikawa, K and Takaoka, T, “Generation of Parenthesis Strings by transpositions,” Proc. The
Computing: The Australasian Theory Symposium (CATS ‘97) (1997) 51-58.
[14] Ruskey, F. and Savage, C., A Gray code for combinations of a multi-set, European Jour.
combinatorics, 68 (1996) 1-8.
[15] Savage, C., A survey of combinatorial Gray codes, SIAM Review, 39 (1997) 605- 629.
[16] Takaoka, T, O(1) Time Algorithms for Combinatorial Generation by Tree Traversal,
Computer Journal 42, 5 (1999) 400-408
[17] Takaoka, T., An O(1) Time Algorithm for Generating Multiset Permutations, ISAAC 1999,
LNCS 1741 (1999)237-246.
[18] Takaoka, T. and Violich, S., Fusing Loopless Algorithms for Combinatorial Generation,
International Journal of Foundations of Computer Science, Vol 18, no 2 (2007)263-293.
[19] Vajnovszki, V., On the loopless generation of binary tree sequences, Info. Proc. Lett., 68
(1998)113-117
[20] Vajnovszki, V., A loopless algorithm for generating the permutations of a multiset,
Theoretical Computer Science, 307 (2) (2003)415-431
[21] Walsh, T.R., Generation of well-formed parenthesis strings in constant worst-case time, Jour.
Algorithms, 29, (1998) 165-173
[22] Wilf, H.S., Combinatorial Algorithms: An Update, SIAM, Philadelphia, 1989.
[23] Williams, A., Loop-less Generation of multiset permutations using a constant
number of variables by prefix shifts, Proc. SODA 2009, pp987-996
[24] Zerling, D, Generating binary trees by rotations, JACM, 32, (1985) 694-701
Appendix 1. Review of Takaoka’s algorithm for in-place combination generation.
We describe an O(1) time algorithm for generating the set S of combinations of n elements out of
the set {1, ..., r}. In the following array “q” is the main container of combinations and “a” is an
auxiliary array for book-keeping; it keeps track of tree traversal with some delay. We state a few
lemmas describing necessary properties for our multi-set permutation generation.
Example . The twisted lexico-tree for combinations of 4 elements out of 6 elements is given with
the contents of array “a” and “q” at the leaves. A white circle is for the even parity and black for
odd.
6 1236 1236
2
4 6 1246 1246
5 1245 1245
5
6 1256 1265
1
5 6 1356 1365
B 3
4 5 1345 1345
F
6 1346 1346
4 5
A 6 1456 1546
4 5 6 2456 2546
2 D
C 4 6 2346 2346
3
5 2345 2345
E 5
6 2356 2365
3 4 5
6 3456 4365
Lemma 1. The set S generated by the twisted lexico-tree generates combinations with one
change per combination. See [16] for a proof.
Now we describe implementation details. There are many nodes which have only one child
down to the leaf, causing straight lines. Traversal of these lines downwards will cause O(n) time.
Having one child is caused by a node with the maximum possible label at the level. To control the
position to which we come down, we keep an array “down”. In the above example, we use “up”
to go from A to B, and use “down” to go from C to D. A snapshot of the movement is like (...,
F,A,B,C,D,E, ...). As we see below, we cannot generate combinations in labels attached to the
nodes of the twisted lexico-tree with a fixed number of changes. Let array a contain those labels.
Then we have a situation with
a = (a1, ... ,ai-1, ai, ... ,aj, ... ,an) and a’ = (a1, ... ,ai-1, ai+1, ... ,aj+1 ... ,an),
where ak+1=ak+1 for i k j, or a symmetric case where a’ = (a1, ... ,ai-1, ai-1, ... ,aj -1, ... ,an),
where ak+1=ak-1. Note that “i” is the difference point and “j” is the solution point. In the first case,
“ai” goes out of the combination and “aj+1” comes into the combination like a revolving door
system. We keep the combinations in array “q” and use array “a” for book-keeping. As we cannot
change j-i+1 places in “a” in O(1) time, we just change ai and aj, and change the contents of q
correspondingly. Thus we do not implement line 11 of Algorithm 1 to prepare for the opposite
path to the next object. To keep track of the positions of these elements in q, we use array ”pos”.
Solution points are maintained in array “solve”. Since some parts of array “a” are not maintained
to the corresponding labels in the tree, we use Boolean array “mark” to show no maintenance.
When mark[i]=true, the proper value of a[i-1] is given by its child a[i]. The main part of the
algorithm follows in which up[i], down[i], solve[i], and pos[i] are initialized to i for all i. The
values of d[i] are initialized to 1 and those of “mark” to false. Line-by-line explanation follows
the algorithm. If we change the termination condition at line 23 to false we can keep generating
combinations in forward order and reverse order alternately forever. The proof of the next lemma
is omitted.
Lemma 2. The generation of combinations in the binary vector form by the above twisted lexico-
tree does not change the relative order of 0’s. Items 1 move from the left end and finish at the
right end for one run in the binary vector. From the second run on, the movement alternates in
direction.
Algorithm 4. In-place algorithm for combinations {arrays a, q, d, up, pos, sol, solve, mark, used}
1. d[n+1]:= -1; up[0]:=0; i:=n; initialize d[i] to 1, and other arrays to i for all i; d[0]:=0;
2. repeat
3. output(q); /* This is to output q */
4. if mark[i]=true then begin a[i-1]:=a[i]-1; mark[i]:=false end;
5. if d[i+1]<0 then begin /* Moving from an even node to an odd node*/
6. q[pos[a[i]]]:=a[i]+d[i]; pos[a[i]+d[i]]:=pos[a[i]]
7. end else begin
8. if d[i]>0 then /* Moving from odd to even, with “a” value increasing */
9. q[pos[a[i]]]:=a[solve[i]]+d[i]; pos[a[solve[i]]+d[i]]:=pos[a[i]]
10. end else if d[i]<0 then begin /* Moving from odd to even, with “a” value decreasing */
11. q[pos[a[solve[i]]]]:=a[i]+d[i]; pos[a[i]+d[i]]:=pos[a[solve[i]]]
12. end;
13. a[i]:=a[i]+d[i];
14. if d[i+1]>0 then a[solve[i]]:=a[solve[i]]+d[i];
15. up[i]:=i;
16. if (d[i]>0) and (a[i]=r-n+i) or (d[i]<0) and (a[i]=a[i-1]+1) then begin
17. up[i]:=up[i-1]; up[i-1]:=i-1;
18. down[up[i]]:=i;
19. if d[i]<0 then begin solve[[up[i]]:=i; mark[i]:=true end;
20. d[i]:= -d[i];
21. if (d[i]<0) or (i=n) then i:=up[i] else i:=down[i]
22. end else i:=down[i]
23. until i=0; { This termination condition is replaced by false for an infinite loop}
24. output(q).
For the readability this program is based on two-dimensional arrays for local variables for
combination generation. Available at http://www.cosc.canterbury.ac.nz/tad.takaoka/multiperm.c