How to represent a list as a cycle
Assuming that your lists contain no duplicates you can use this:
set =
{{a, b, c, d}, {a, b, d, c}, {a, c, b, d}, {a, c, d, b}, {a, d, b, c},
{a, d, c, b}, {b, a, c, d}, {b, a, d, c}, {b, c, a, d}, {b, c, d, a},
{b, d, a, c}, {b, d, c, a}, {c, a, b, d}, {c, a, d, b}, {c, b, a, d},
{c, b, d, a}, {c, d, a, b}, {c, d, b, a}, {d, a, b, c}, {d, a, c, b},
{d, b, a, c}, {d, b, c, a}, {d, c, a, b}, {d, c, b, a}};
DeleteDuplicates[
RotateLeft[#, Ordering[#,1] - 1] & /@ set
]
{{a, b, c, d}, {a, b, d, c}, {a, c, b, d}, {a, c, d, b}, {a, d, b, c}, {a, d, c, b}}
Extension to lists with duplicates
A pairwise comparison with SameTest
will always be slower than placing elements into a canonical form and using Union
or DeleteDuplicates
with the default algorithms. To that end I propose this for lists that may have duplicates:
canonize[a_List] :=
With[{X = # ~Extract~ Ordering[#, 1] &},
RotateLeft[a, # - 1] & /@ Position[a, X @ a] // X
]
Example:
SeedRandom[1];
set =
Join[
Table[RandomSample[{a, b, c, d}, 4], {20}],
RandomChoice[{a, b, c, d}, {5, 4}]
];
Union[canonize /@ set]
{{a, a, a, c}, {a, b, c, d}, {a, b, d, c}, {a, c, a, d}, {a, c, b, d}, {a, c, d, b}, {a, d, b, b}, {a, d, b, c}, {a, d, c, b}, {b, c, b, c}}
This is far faster than a pairwise compare:
rotatedQ[{x___}, {y___}] := (* example function provided by whuber *)
Length[{x}] == Length[{y}] && MatchQ[{y, y}, {___, x, ___}];
big = RandomInteger[4, {5000, 5}];
Union[canonize /@ big] // Length // Timing
Union[big, SameTest -> rotatedQ] // Length // Timing
{0.047, 629}
{5.007, 629}
A solution using the built-in Cycles
comparison:
numbering = MapIndexed[#1 -> #2[[1]] &, Sort[set[[1]]]];
DeleteDuplicates[set, Cycles[{#1}] == Cycles[{#2}] /. numbering &]