From e321f2c62cb98961de4af9d8923859d226185530 Mon Sep 17 00:00:00 2001 From: Torben Wiedemann <7226617+TWiedemann@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:38:10 +0100 Subject: [PATCH 1/3] NiceGeneratorsSn: Add comments, give variables more explicit names, make some steps easier to read, split up the 2 random searches --- gap/perm/giant.gi | 108 ++++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 36 deletions(-) diff --git a/gap/perm/giant.gi b/gap/perm/giant.gi index d88d8693..c639532a 100644 --- a/gap/perm/giant.gi +++ b/gap/perm/giant.gi @@ -23,51 +23,83 @@ SetInfoLevel( InfoGiants, 1 ); ####################################################################### ## -#F NiceGeneratorsSn(,,) ...... find n-cycle, transposition -## +#F NiceGeneratorsSn(,) ...... find n-cycle, transposition ## +## For a permutation group grp on n points, this function tries to return a list +## [ fullCycle, transp ] consisting of an n-cycle fullCycle in grp and a transposition +## tranps in grp which interchanges successive points of fullCycle. +## (It then follows that grp is the full permutation group on n points and that +## [ fullCycle, transp ] is a generating set of grp.) +## Thus up to renaming the points, fullCycle = (1,...,n) and transp = (1,2). +## If n<=2, returns fail. +## This is done by drawing at most N random elements of grp. +## If no such generating set is found, the function returns fail. -RECOG.NiceGeneratorsSn := function ( mp, grp, N ) +RECOG.NiceGeneratorsSn := function ( grp, N ) - local t, cyclen, g, y, h, i, x, supp, n; + local cyclen, fullCycle, transp, rand, orders, i, supp, suppNew, n, mp, + numTries; + mp := MovedPoints(grp); n := Length(mp); - while N > 0 do - N := N - 1; - t := PseudoRandom( grp ); - # was: cyclen := Collected( CycleLengths( t, [1..n] ) ); - cyclen := CycleStructurePerm(t); - # was: if IsBound(g) = false and [n,1] in cyclen then - if IsBound(g) = false and IsBound(cyclen[n-1]) then - # we found an $n$-cycle - g := t; - fi; + if n<=2 then + return fail; + fi; - if IsBound(y) = false and IsBound(cyclen[1]) and cyclen[1] = 1 and - ForAll([1..QuoInt(n,2)-1],i->not IsBound(cyclen[2*i+1])) - # was: Filtered( cyclen, x -> x[1] mod 2 = 0 ) = [ [ 2,1 ] ] - then - # we can get a transposition - y := t^(Lcm(Filtered([2..n],x->IsBound(cyclen[x-1])))/2); + # Random search for an n-cycle and a transposition in grp + fullCycle := fail; + transp := fail; + while (fullCycle = fail or transp = fail) and N > 0 do + N := N - 1; + rand := PseudoRandom( grp ); + # If cyclen[i] is bound, it is the number of cycles of length i+1 in rand. + # Otherwise rand has no cycle of length i+1. + cyclen := CycleStructurePerm(rand); + # Is rand an n-cycle? + # Check that at least one n-cycle appears in the cycle decomposition of rand. + # In this case, rand must be equal to this n-cycle. + if fullCycle = fail and IsBound(cyclen[n-1]) then + fullCycle := rand; fi; - - if IsBound(g) and IsBound(y) then - i := 10*n; - supp := MovedPoints( y ); - while i > 0 do # take up to i conjugates, - # check if they match the $n$-cycle - i := i-1; - x := PseudoRandom( grp ); - if (supp[1]^x)^g = supp[2]^x or (supp[2]^x)^g = supp[1]^x - then h := y^x; - return [ g, h ]; + # Can we construct a transposition from rand? + # Check that the cycle decomposition of rand contains exactly one 2-cycle + # and no cycle of length k where k is even and 4<=k2 such that rand contains a k-cycle. + # Since rand contains a 2-cycle, we only have to check up to n-2. + orders := Filtered([3..n-2],x->IsBound(cyclen[x-1])); + if ForAll(orders, IsOddInt) then + if IsEmpty(orders) then # rand is a 2-cycle + transp := rand; + else + # Raising to the power of Lcm(orders) kills all cycles of + # odd length. The single 2-cycle in rand remains + transp := rand^(Lcm(orders)); fi; - od; - return fail; + fi; fi; + od; - od; # loop over random elements + # Conjugate transp by random elements until we obtain a transposition that + # interchanges successive elements of fullCycle. + if fullCycle <> fail and transp <> fail then + numTries := 10*n; # TODO: Why do we choose this constant? + supp := MovedPoints( transp ); # Support of transp + while numTries > 0 do + numTries := numTries-1; + rand := PseudoRandom( grp ); + suppNew := List(supp, x->x^rand); # Support of transp^rand + # Check that transp^rand interchanges successive points of fullCycle, + # or equivalently, that fullCycle moves one point of transp^rand + # to the other one. + if suppNew[1]^fullCycle = suppNew[2] + or suppNew[2]^fullCycle = suppNew[1] + then + return [ fullCycle, transp^rand ]; + fi; + od; + fi; return fail; @@ -125,9 +157,11 @@ RECOG.RecogniseSn := function( mp, grp, eps ) le := le + 1; od; + # TODO: Document these magic constants. They are probably from some paper. + # Also, Max Horn suggested that it may be better to use a smaller N. N := Int(24 * (4/3)^3 * le * 6 * n); - gens := RECOG.NiceGeneratorsSn( mp, grp, N ); + gens := RECOG.NiceGeneratorsSn( grp, N ); if gens = fail then Info(InfoGiants,1,"couldn't find nice generators for Sn"); return fail; @@ -441,6 +475,7 @@ end; ## ## ## This is the main function. +## TODO: Document the precise meaning of the threshold eps. Does it come from a specific paper? ## RECOG.RecogniseGiant := function( mp, grp, eps ) @@ -694,7 +729,8 @@ RECOG.SLPforAn := function( n, pi ) end; - +# TODO: Document this magic constant properly. +# (It is apparently the default for eps in FindHomMethodsPerm.) RECOG.GiantEpsilon := 1/1024; #! @BeginChunk Giant From aeb4264c3aa4f179ec9c87571406266e8c08a81d Mon Sep 17 00:00:00 2001 From: Torben Wiedemann <7226617+TWiedemann@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:38:32 +0100 Subject: [PATCH 2/3] Add tests for NiceGeneratorsSn --- tst/working/quick/PermNiceGens.tst | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 tst/working/quick/PermNiceGens.tst diff --git a/tst/working/quick/PermNiceGens.tst b/tst/working/quick/PermNiceGens.tst new file mode 100644 index 00000000..7b10b761 --- /dev/null +++ b/tst/working/quick/PermNiceGens.tst @@ -0,0 +1,50 @@ +# +gap> START_TEST("PermNiceGens.tst"); +gap> testNiceGeneratorsSn := function(grp) +> local Sn, m, niceGens, fullCycle, transp, cyclen1, x, y, i; +> m := Size(MovedPoints(grp)); +> niceGens := RECOG.NiceGeneratorsSn(grp, 10000); +> if m<=2 then +> if niceGens = fail then +> return; # everything ok +> else +> Display("NiceGeneratorsSn should return fail for n<=2"); +> fi; +> elif niceGens <> fail then +> fullCycle := niceGens[1]; +> transp := niceGens[2]; +> if Size(MovedPoints(transp)) <> 2 then +> Display("Second generator is not a transposition"); +> fi; +> cyclen1 := CycleStructurePerm(niceGens[1]); +> for i in [1..m-2] do +> if IsBound(cyclen1[i]) then +> Display("First generator is not a full cycle"); +> fi; +> od; +> x := MovedPoints(transp)[1]; +> y := MovedPoints(transp)[2]; +> if not (x^fullCycle = y or y^fullCycle = x) then +> Display("Transposition does not interchange successive points of full cycle"); +> fi; +> fi; +> end;; + +# Test that NiceGeneratorsSn finds correct generators +gap> for n in [1..50] do +> Sn := SymmetricGroup(n); +> for j in [1..3] do # Test a few times +> testNiceGeneratorsSn(Sn); +> testNiceGeneratorsSn(Group(Random(Sn), Random(Sn))); +> od; +> od; + +# Test that NiceGeneratorsSn returns fail for some groups that +# are not a full symmetric group +gap> grps := [Group((1,2,3)), Group((1,2), (3,4)), Group((1,10),(2,15,30))];; +gap> for G in grps do if RECOG.NiceGeneratorsSn(G, 10000) <> fail then +> Print("NiceGeneratorsSn should not succeed for ", G, "\n"); +> fi; od; + +# +gap> STOP_TEST("PermNiceGens.tst"); From c132fe10620b52716f39e578303f62fd5eaa7bb7 Mon Sep 17 00:00:00 2001 From: Torben Wiedemann <7226617+TWiedemann@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:08:10 +0100 Subject: [PATCH 3/3] Improve documentation of NiceGeneratorsSn --- gap/perm/giant.gi | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/gap/perm/giant.gi b/gap/perm/giant.gi index c639532a..8427b52b 100644 --- a/gap/perm/giant.gi +++ b/gap/perm/giant.gi @@ -25,15 +25,20 @@ SetInfoLevel( InfoGiants, 1 ); ## #F NiceGeneratorsSn(,) ...... find n-cycle, transposition ## -## For a permutation group grp on n points, this function tries to return a list -## [ fullCycle, transp ] consisting of an n-cycle fullCycle in grp and a transposition -## tranps in grp which interchanges successive points of fullCycle. -## (It then follows that grp is the full permutation group on n points and that -## [ fullCycle, transp ] is a generating set of grp.) -## Thus up to renaming the points, fullCycle = (1,...,n) and transp = (1,2). -## If n<=2, returns fail. -## This is done by drawing at most N random elements of grp. -## If no such generating set is found, the function returns fail. +## For a permutation group grp on n points, this function returns either fail +## or a list [ fullCycle, transp ] such that the following hold: +## - n > 2. +## - fullCycle is an n-cycle. +## - transp is a transposition, i.e., a 2-cycle. +## - transp interchanges two successive points of fullCycle. In other words, +## fullCycle = (1,...,n) and transp = (1,2) up to renaming the n points. +## The conditions on [ fullCycle, transp ] imply that grp is the full symmetric +## group on n points. Thus the function always returns fail if grp is not the +## full symmetric group, and also if n <= 2. +## fullCycle, transp are found by a random search, drawing at most N random +## elements from grp. +## Thus if grp is the full symmetric group on n points, then the function does not +## return fail with a probability that grows as N grows. RECOG.NiceGeneratorsSn := function ( grp, N )