The final example returns to the stable marriage problem. Suppose you are asked to find a stable pairing for the n men and women of the stable marriage problem in Chapter 2, but you aren't aware of the algorithm discussed in Section 2.5. Probably the most straightforward approach is to search through all the possible pairings until a stable pairing is found. Each possible pairing may be thought of as a specific permutation of the n women. Thus the example pairing of Table 2.2 corresponds to the permutation (or listing) 4, 3, 1, 2, 5. In general there are n ! such permutations (where n! means n factorial).
This approach yields a program that traverses the collection of permutations, processing each one and halting when a stable permutation is encountered. Stable may be conveniently invoked to check a permutation for stability. In contrast, the algorithm of Section 2.5 avoids such an exhaustive search by eliminating some permutations from consideration altogether. Still, many problems require just such an exhaustive search and contain a component to produce the permutations. The problem is to write a program to carry out the traversal. The function permutations does this:
permutations(n)
/* Generates and processes each
permutation of the integers
1 to n.
*/
int n;
{
int done;
done = FALSE;
initialize(n);
while (!done)
{
process(n);
next(n,&done);
}
}
where next (n,&done) must return, having updated the current permutation to the next permutation, unless the current permutation is the last one, when it simply sets done to true.
The problem is now to refine next. Suppose n = 4 and the first permutation is 3 2 1. Inserting 4 in all possible positions among 1, 2, and 3 gives the first four permutations of 1, 2, 3, and 4.
4 3 2 1
3 4 2 1
3 2 4 1
3 2 1 4
Removing 4 leaves 3 2 1. The next four permutations for n = 4 can be obtained by first obtaining the next permutation after 3 2 1 for n = 3. But this next permutation for n = 3 can similarly be obtained by shifting 3 to the right one place, yielding 2 3 1. Then the next four permutations for n = 4 are
4 2 3 1
2 4 3 1
2 3 4 1
2 3 1 4
Removing 4 leaves 2 3 1. Again, the next permutation after 2 3 1, 2 1 3, is obtained by shifting 3 right. The next four permutations for n = 4 are
4 2 1 3
2 4 1 3
2 1 4 3
2 1 3 4
Continuing this process will produce, in turn,
4 3 1 2 4 1 3 2 4 1 2 3
3 4 1 2 1 4 3 2 1 4 2 3
3 1 4 2 1 3 4 2 1 2 4 3
3 1 2 4 1 3 2 4 and 1 2 3 4
Generalizing, we see that, given a permutation of the n integers from 1 to n, the next permutation can be generated by shifting n to the right whenever possible. The only time this is not possible is if n is already at the right end. The next permutation must then be generated by removing n and replacing the leftmost n - 1 integers, which must be a permutation of the integers from 1 to n - 1, by their next permutation. If n is 1, then the last permutation has been produced. This provides a recursive definition for next.
In order to proceed, a data structure to store the permutation must be chosen. To illustrate the effect of this selection, we will use two different structures?/FONT>arrays and lists. We start by storing the permutation in an array p. Searching for the location of n in p will be avoided by keeping an array l of pointers. Both p and l are defined globally. l[n] points to the location of n in p when next(n,pdone) is invoked. Initialize must set p[i] to n - i + 1 and l[i] to 1 for 1 ?/FONT> i ?/FONT> n. Next may be defined as
Figure 4.6(a) simulates the execution of next(4,pdone) when the current permutation is 2 1 3 4.
Now suppose p is implemented as a list whose records are stored in dynamic memory. We need the pointer arrays ptr and pred, which are defined globally, as is the record pointer p. When next(n,pdone) is invoked, ptr[n] will contain a pointer to the record containing n in its information field, and pred[n] will contain a pointer to the predecessor of that record in p. To simplify list shifts and the insertion of a record at the front of p, a dummy record is used as the first record of p. Pred[k] will initially contain a pointer to the dummy record, and ptr[k] will initially contain a pointer to the record of p that contains k, for l ?/FONT> k ?/FONT> n. Now next may be defined as follows:
Figure 4.6(b) simulates the execution of next(4,pdone) when the current permutation is 2 l 3 4.
In comparing the two implementations notice that the advantage of the list implementation is that the for loop required by the array implementation is no longer needed. Inserting n at the left end of the current permutation thus takes only two pointer changes rather than time proportional to n. Other permutation algorithms are explored in Sedgewick [1977].
To further illustrate the differences, two complete programs using function permutations, one for the array and one for the list implementation, are given below. The process function prints each permutation. To adapt these programs to obtain a solution for the stable marriage problem, the function process must test for stability. Another modification would be to abort the execution when a stable pairing is found. This requires that process be given access to done so that it can set done to true at that point.
Array Implementation
List Implementation
next (n,pdone)
If n is not 1, then
If n is not at the right end of the current permutation of 1, . . , n then
shift it right one position
else
remove "n" from the current permutation of 1, . . , n
next(n - 1,pdone)
Insert "n" at the left end of the current permutation of 1, . . , n - 1
else
set done to true.
next(n,pdone)
/* Generates the next permutation unless
the current one is the last. In this case
done is set to true.
*/
int n,*pdone;
{
int i;
if (n > 1)
if (l[n] < n)
{
p[l[n]] = p[l[n]+1];
p[l[n]+1] = n;
l[n] = l[n]+1;
}
else
{
next(n-1,pdone);
for (i=n-1;i>=1;i--)
p[i+1] = p[i];
p[1] = n;
l[n] = 1;
}
else
*pdone = TRUE;
}
next(n,pdone)
/* Generates the next permutation unless
the current one is the last. In this case
done is set to true.
*/
int n,*pdone;
{
if (n > 1)
if (ptr[n]->link != NULL)
{
pred[n]->link = ptr[n]->link;
pred[n] = pred[n]->link;
ptr[n]->link = pred[n]->link;
pred[n]->link = ptr[n];
}
else
{
pred[n]->link = NULL;
next(n-l,pdone);
ptr[n]->link = p->link;
p->link = ptr[n];
pred[n] = p;
}
else
*pdone = TRUE;
}
(a) Array Implementation for P
(b) List Implementation for P
Figure 4.6 Simulation of NEXT (4,&DONE) When P is 2134
#include <stdio.h>
#define TRUE 1
#define FALSE 0
typedef int arraytype[21];
arraytype p,l;
main()
/* Driver for permutations. */
{
int n;
printf("\n n = ?\n");
scanf("%d",&n);
permutations(n);
}
permutations(n)
/* Generates and processes each
permutation of the integers
1 to n.
*/
int n;
{
int done;
done = FALSE;
initialize(n);
while (!done)
{
process(n);
next(n,&done);
}
}
initialize(n)
/* Creates initial permutation */
int n;
{
int i;
for (i = 1;i<=n;i++)
{
p[i] = n - i + 1;
l[i] = 1;
}
}
process(n)
/* Prints the current permutation */
int n;
{
int i;
for (i=1;i<=n;i++)
printf("\n%d\n",p[i]);
printf("\n");
}
next(n,pdone)
/* Generates the next permutation unless
the current one is the last. In this case
done is set to true.
*/
int n,*pdone;
{
int i;
if (n > 1)
if (l[n] < n)
{
p[l[n]] = p[l[n]1];
p[l[n]+1] = n;
l[n] = l[n]+1;
}
else
{
next(n-1,pdone);
for (i=n-1;i>=1;i--)
p[i+1] = p[i];
p[1] = n;
l[n] = 1;
}
else
*pdone = TRUE;
}
#include <stdio.h>
#define TRUE l
#define FALSE 0
#define NULL 0
typedef struct record
{
int info;
struct record *link;
}listrecord,*listpointer;
typedef listpointer pointerarray[21];
pointerarray ptr,pred;
listpointer p;
main()
/* Driver for permutations. */
{
int n;
printf("\n n = ?\n");
scanf("%d",&n);
permutations(n);
}
permutations(n)
/* Generates and processes each
permutation of the integers 1 to n.
*/
int n;
{
int done;
done = FALSE;
initialize(n);
while (!done)
{
process(n);
next(n,&done);
}
}
initialize(n)
/* Creates initial permutation */
int n;
{
int i;
listpointer q;
p = malloc(sizeof(listrecord));
q = P;
for (i=1;i<=n;i++)
{
q->link = malloc(sizeof(listrecord));
q = q->link;
q->info = n - i + 1;
ptr[n-i+1] = q;
pred[i] = p;
}
q->link = NULL;
}
process(n)
/* Prints the current permutation */
int n;
{
listpointer q;
q = p->link;
while (q != NULL)
{
printf("\n%d\n",q->info);
q = q->link;
}
printf("\n");
}
next(n,pdone)
/* Generates the next permutation unless
the current one is the last. In this case
done is set to true.
*/
int n,*pdone;
{
if (n > 1)
if (ptr[n]->link != NULL)
{
pred[n]->link = ptr[n]->link;
pred[n] = pred[n]->link;
ptr[n]->link = pred[n]->link;
pred[n]->link = ptr[n];
}
else
{
pred[n]->link = NULL;
next(n-1,pdone);
ptr[n]->link = p->link;
p->link = ptr[n];
pred[n] = p;
}
else
*pdone = TRUE;
}