Skip to content

Commit

Permalink
Speed up Printing permutations
Browse files Browse the repository at this point in the history
Replace old O(n^2) algorithm with O(n) one, because unlike the
comment, we are no longer limited to 9600 baud and can produce some
large permutations
  • Loading branch information
ChrisJefferson authored and fingolfin committed Nov 14, 2019
1 parent 1fd0e1a commit 012238d
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 12 deletions.
25 changes: 14 additions & 11 deletions src/permutat.cc
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,8 @@ static Obj InvPerm(Obj perm);
** uses the degree to print all points with same width, which looks nicer.
** Linebreaks are prefered most after cycles and next most after commas.
**
** It does not remember which points have already been printed. To avoid
** printing a cycle twice each is printed with the smallest element first.
** This may in the worst case, for (1,2,..,n), take n^2/2 steps, but is fast
** enough to keep a terminal at 9600 baud busy for all but the extrem cases.
** It remembers which points have already been printed, to avoid O(n^2)
** behaviour.
*/
template <typename T>
static void PrintPerm(Obj perm)
Expand All @@ -187,29 +185,34 @@ static void PrintPerm(Obj perm)
else if ( degPerm < 10000 ) { fmt1 = "%>(%>%4d%<"; fmt2 = ",%>%4d%<"; }
else { fmt1 = "%>(%>%5d%<"; fmt2 = ",%>%5d%<"; }

UseTmpPerm(SIZE_OBJ(perm));
T * ptSeen = ADDR_TMP_PERM<T>();

// clear the buffer bag
for (p = 0; p < DEG_PERM<T>(perm); p++)
ptSeen[p] = 0;

/* run through all points */
isId = 1;
ptPerm = CONST_ADDR_PERM<T>(perm);
for ( p = 0; p < degPerm; p++ ) {

/* find the smallest element in this cycle */
q = ptPerm[p];
while ( p < q ) q = ptPerm[q];

/* if the smallest is the one we started with lets print the cycle */
if ( p == q && ptPerm[p] != p ) {
if (!ptSeen[p] && ptPerm[p] != p) {
ptSeen[p] = 1;
isId = 0;
Pr(fmt1,(Int)(p+1),0L);
ptPerm = CONST_ADDR_PERM<T>(perm);
for ( q = ptPerm[p]; q != p; q = ptPerm[q] ) {
ptSeen[q] = 1;
Pr(fmt2,(Int)(q+1),0L);
ptPerm = CONST_ADDR_PERM<T>(perm);
ptSeen = ADDR_TMP_PERM<T>();
}
Pr("%<)",0L,0L);
/* restore pointer, in case Pr caused a garbage collection */
ptPerm = CONST_ADDR_PERM<T>(perm);
ptSeen = ADDR_TMP_PERM<T>();
}

}

/* special case for the identity */
Expand Down
13 changes: 12 additions & 1 deletion tst/testinstall/perm.tst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#@local checklens,n,permAll,permBig,permSml,x,y, moved
#@local checklens,n,permAll,permBig,permSml,x,y,moved,p
gap> START_TEST("perm.tst");

# Permutations come in two flavors in GAP, with two TNUMs: T_PERM2 for
Expand Down Expand Up @@ -87,6 +87,17 @@ gap> Print(permBig * (5,999999), "\n");
( 1, 2, 3)( 5,999999), ( 1, 3, 2)( 5,999999),
( 1, 3)( 5,999999) ]

# Check String of large permutation
# Check length, beginning and end, as the string is too long to include in
# a test file
gap> p := String(PermList(Concatenation([2..2^20], [1])));;
gap> Length(p) = 7277505;
true
gap> p{[1..40]};
"(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,"
gap> p{[Length(p)-40..Length(p)]};
",1048572,1048573,1048574,1048575,1048576)"

#
# EqPerm
#
Expand Down

0 comments on commit 012238d

Please sign in to comment.