{"id":13442665,"url":"https://github.com/stylewarning/cl-permutation","last_synced_at":"2026-01-06T05:50:45.712Z","repository":{"id":46278797,"uuid":"121821101","full_name":"stylewarning/cl-permutation","owner":"stylewarning","description":"Permutations and permutation groups in Common Lisp.","archived":false,"fork":false,"pushed_at":"2023-07-08T22:50:10.000Z","size":488,"stargazers_count":50,"open_issues_count":15,"forks_count":6,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-25T23:34:10.380Z","etag":null,"topics":["lisp","permutation-algorithms","permutation-groups","permutations"],"latest_commit_sha":null,"homepage":null,"language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stylewarning.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-02-17T02:25:50.000Z","updated_at":"2024-11-02T09:13:28.000Z","dependencies_parsed_at":"2024-01-15T04:11:39.200Z","dependency_job_id":null,"html_url":"https://github.com/stylewarning/cl-permutation","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylewarning%2Fcl-permutation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylewarning%2Fcl-permutation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylewarning%2Fcl-permutation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylewarning%2Fcl-permutation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stylewarning","download_url":"https://codeload.github.com/stylewarning/cl-permutation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245708990,"owners_count":20659625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lisp","permutation-algorithms","permutation-groups","permutations"],"created_at":"2024-07-31T03:01:48.843Z","updated_at":"2026-01-06T05:50:45.665Z","avatar_url":"https://github.com/stylewarning.png","language":"Common Lisp","readme":"# CL-PERMUTATION\n\nA library for operating on permutations and permutation groups.\n\n\n## Creating Permutations\n\nPermutations are represented by the structure `PERM`, which is\nread-only/immutable at the API boundary. A permutation of size `N` is essentially a sequence\nof numbers from `1` to `N`. One-based permutations were chosen because that\nis the dominating convention in mathematics. All we lose, essentially,\nis direct compatibility with array indexing, and one fixnum worth of\nspace. (Internally, the permutations are stored in an array of size\n`N+1`, where the zeroth element is always zero).\n\nA permutation can be created via `MAKE-PERM`:\n\n```\nPERM\u003e (make-perm 1 2 3)\n#\u003cPERM 1 2 3\u003e\n```\n\nThe permutation will be checked for validity.\n\n```\nPERM\u003e (make-perm 1 2 5)\nGiven permutation must contain the numbers 1 to 3\n   [Condition of type SIMPLE-ERROR]\n```\n\nOne can also create permutations with `LIST-TO-PERM`, which converts a\nlist to a permutation. The companion function `PERM-TO-LIST` does the\nopposite operation, but it's not recommended to use list\nrepresentations of permutations.\n\nOne can also create permutations with `VECTOR-TO-PERM`, which is\nanalogous to `LIST-TO-PERM`, except it works for vectors. The reverse is\n`PERM-TO-VECTOR`.\n\nLastly, there is an experimental reader macro for permutations, which\nare created at read time. To enable the syntax, use\n\n```\n(enable-perm-reader)\n```\n\nand then one may type\n\n```\n#[3 1 2 4 5]\n```\n\nfor permutations instead.\n\n\n## Permutation Operations\n\nThere is a slew of permutation operations:\n\n* `perm-identity`: construct an identity perm\n* `perm-identity-p`: check if a perm is an identity perm\n* `random-perm`: construct a random perm with specified parity\n* `perm-ref`: zero-based reference\n* `perm-eval`: one-based (standard) reference\n* `perm-eval*`: one-based (standard) reference with out-of-bounds handling\n* `perm-inverse-eval`: one-based (standard) reference of inverse\n* `perm-inverse-eval*`: one-based (standard) reference of inverse with out-of-bounds handling\n* `perm=`: check for equality\n* `perm=*`: check for equality of different sized perms\n* `perm-size`: the size of the permutation (number of mapped elements)\n* `perm-length`: number of inversions\n* `perm-even-p`: check for evenness/oddness\n* `perm-odd-p`: ''\n* `perm-sign`: ''\n* `perm-compose`: compose two permutations\n* `perm-expt`: compose a perm with itself a number of times\n* `perm-order`: order of a permutation\n* `perm-transpose-indexes`: swap two indexes, keeping the entries fixed\n* `perm-transpose-entries`: swap two entries, keeping the indexes fixed\n* `perm-inverse`: invert a permutation\n* `perm-fixpoints`: compute the fixed points of a permutation\n* `permute`: permute an array according to a permutation\n* `commutesp`: check if two permutations commute\n* `perm\u003c`: check lexicographic order\n\n\n## Permutation Generation\n\nThere are ways of efficiently generating all permutations of a given\nlength incrementally. Instead of generating all permutations at once\nin memory -- which takes `O(n*n!)` space -- we generate permutations on\nthe fly.\n\nThe first way is to iterate over the permutations using a `DOLIST`-style\nmacro called `DOPERMS`.\n\n```\nPERM\u003e (let ((i 1))\n        (doperms (p 3)\n          (format t \"~D: ~A~%\" i p)\n          (incf i)))\n1: #\u003cPERM 1 2 3\u003e\n2: #\u003cPERM 1 3 2\u003e\n3: #\u003cPERM 3 1 2\u003e\n4: #\u003cPERM 3 2 1\u003e\n5: #\u003cPERM 2 3 1\u003e\n6: #\u003cPERM 2 1 3\u003e\n```\n\nThe other way is to produce a generator object (a closure, in fact)\nwhich generates the permutations. Simply `FUNCALL` the object to receive\nthe next permutation. When they're all exhausted, the closure will\nreturn `NIL`.\n\n```\nPERM\u003e (defparameter S3 (make-perm-generator 3))\nS3\nPERM\u003e (defparameter S2 (make-perm-generator 2))\nS2\nPERM\u003e (list (funcall S2) (funcall S3))\n(#\u003cPERM 1 2\u003e #\u003cPERM 1 2 3\u003e)\nPERM\u003e (list (funcall S2) (funcall S3))\n(#\u003cPERM 2 1\u003e #\u003cPERM 1 3 2\u003e)\nPERM\u003e (list (funcall S2) (funcall S3))\n(NIL #\u003cPERM 3 1 2\u003e)\nPERM\u003e (list (funcall S2) (funcall S3))\n(NIL #\u003cPERM 3 2 1\u003e)\nPERM\u003e (list (funcall S2) (funcall S3))\n(NIL #\u003cPERM 2 3 1\u003e)\n```\n\n\n## Cycle Operations\n\nThere's also a number of operations for cycles. Cycles are represented\nby the `CYCLE` structure. We can convert to and from cycle\nrepresentation using `TO-CYCLES` and `FROM-CYCLES`. Cycles created by\n`TO-CYCLES` are automatically canonicalized with\n`CANONICALIZE-CYCLES`. Canonicalization is defined as:\n\n  * Cycles contain their least element positionally first.\n  * Cycles are listed in descending order of their first element.\n  * No null cycles exist.\n  * The sum of the cycle lengths of a decomposition of a permutation\n    of size `N` is `N`.\n\nCycles that have not been canonicalized are printed with an\nasterisk '`*`'. We can observe this by explicitly disabling cycle\ncanonicalization:\n\n```\nPERM\u003e (make-cycle 3 1)\n#\u003cCYCLE (1 3)\u003e                ; no asterisk\nPERM\u003e (let ((*canonicalize-cycle-on-creation* nil))\n        (make-cycle 3 1))\n#\u003cCYCLE (3 1)*\u003e               ; asterisk\n```\n\nAn example use of `TO-CYCLES` is as follows:\n\n```\nPERM\u003e (let ((r (random-perm 10)))\n        (values r (to-cycles r)))\n#\u003cPERM 7 4 8 5 2 10 3 9 1 6\u003e\n(#\u003cCYCLE (6 10)\u003e #\u003cCYCLE (2 4 5)\u003e #\u003cCYCLE (1 7 3 8 9)\u003e)\n```\n\n`FROM-CYCLES` allows the specification of the permutation's length. For example:\n\n```\nPERM\u003e (from-cycles (list (make-cycle 1 3 2)))\n#\u003cPERM 3 1 2\u003e\nPERM\u003e (from-cycles (list (make-cycle 1 3 2)) 5)\n#\u003cPERM 3 1 2 4 5\u003e\n```\n\nLastly, there is a (mostly useless) function `CYCLES-TO-ONE-LINE` which\nconverts cycles to one-line notation. That is, the cycles\n\n```\n(1 2 3)(4 5)\n```\n\ngets converted to the permutation\n\n```\n12345.\n```\n\nFor example,\n\n```\nPERM\u003e (cycles-to-one-line (list (make-cycle 1 2 3)\n                                (make-cycle 4 5)))\n#\u003cPERM 1 2 3 4 5\u003e\n```\n\nIf one takes a permutation which has been canonically decomposed into\ncycles, then interestingly, there exists a bijection between one-line\nnotation and the cycle decomposition.\n\n\n## Combinatorial Specifications\n\nA \"combinatorial specification\" describes a space of combinatorial\nobjects. They have a nice property that they all can be mapped to and\nfrom integers sharply. See the section \"Ranking and Unranking\nCombinatorial Specifications\".\n\n\nCurrently, only objects of linear structure exist. All of them are\nrepresented as subclasses of `COMBINATORIAL-SPEC`. They are as follows.\n\n\n### `RADIX-SPEC`: Base-`B` Non-Negative Integers\n\nThese are a representation of a base-`B` non-negative integer, for a\nbase `B \u003e 1`. They are handled by the `RADIX-SPEC` class. Within\n`CL-PERMUTATION`, the digits are written left-to-right to correspond\nwith natural vector index ordering. A `RADIX-SPEC` can be made with\n`MAKE-RADIX-SPEC`. Here is the enumeration of all two-digit trinary\nnumbers:\n\n```\nPERM\u003e (print-objects-of-spec (make-radix-spec 3 2))\n0 ==\u003e #(0 0) ==\u003e 0\n1 ==\u003e #(1 0) ==\u003e 1\n2 ==\u003e #(2 0) ==\u003e 2\n3 ==\u003e #(0 1) ==\u003e 3\n4 ==\u003e #(1 1) ==\u003e 4\n5 ==\u003e #(2 1) ==\u003e 5\n6 ==\u003e #(0 2) ==\u003e 6\n7 ==\u003e #(1 2) ==\u003e 7\n8 ==\u003e #(2 2) ==\u003e 8\n```\n\n### `MIXED-RADIX-SPEC`: Non-Negative Mixed-Radix Integers\n\nA mixed-radix integer is a generalization of a base-`B` integer. The\ndigits in a mixed-radix numeral correspond to different\nbases. Mixed-radix specifications can be made with\n`VECTOR-TO-MIXED-RADIX-SPEC`. For example, the following are all\nnumerals of radix `(2, 3, 1)`:\n\n```\nPERM\u003e (print-objects-of-spec (vector-to-mixed-radix-spec #(2 3 1)))\n0 ==\u003e #(0 0 0) ==\u003e 0\n1 ==\u003e #(1 0 0) ==\u003e 1\n2 ==\u003e #(0 1 0) ==\u003e 2\n3 ==\u003e #(1 1 0) ==\u003e 3\n4 ==\u003e #(0 2 0) ==\u003e 4\n5 ==\u003e #(1 2 0) ==\u003e 5\n```\nNotice again we use vector index ordering.\n\n\n### `PERM-SPEC`: Permutations\n\nThe space of permutations of length `N` (also known as `S_N`) can be\nrepresented. These are represented by the `PERM-SPEC` class.\n\n```\nPERM\u003e (print-objects-of-spec (make-perm-spec 3))\n0 ==\u003e #(0 1 2) ==\u003e 0\n1 ==\u003e #(0 2 1) ==\u003e 1\n2 ==\u003e #(1 0 2) ==\u003e 2\n3 ==\u003e #(1 2 0) ==\u003e 3\n4 ==\u003e #(2 0 1) ==\u003e 4\n5 ==\u003e #(2 1 0) ==\u003e 5\n```\n\nCurrently, actual `PERM` objects are *not* generated (see below about\nranking/unranking). However, one can easily convert between the two.\n\n\n### `COMBINATION-SPEC`: Combinations\n\nCombinations represent the selection of `M` objects from a collection of\n`N` objects. These are represented by a vector containing `M` `1`'s and `N`\n`0`'s. The class that manages this is a `COMBINATION-SPEC`. For example,\nall combinations of 2 objects of a total of 4 can be listed by the\nfollowing:\n\n```\nPERM\u003e (print-objects-of-spec (make-combination-spec 4 2))\n0 ==\u003e #(0 0 1 1) ==\u003e 0\n1 ==\u003e #(0 1 0 1) ==\u003e 1\n2 ==\u003e #(1 0 0 1) ==\u003e 2\n3 ==\u003e #(0 1 1 0) ==\u003e 3\n4 ==\u003e #(1 0 1 0) ==\u003e 4\n5 ==\u003e #(1 1 0 0) ==\u003e 5\n```\n\n### `WORD-SPEC`: Words\n\nA word is similar to a permutation except that it may have repeated,\nindistinct elements. These are represented by a `WORD-SPEC`. It can be\ncreated by supplying a sample word to the function\n`VECTOR-TO-WORD-SPEC`. For example, all words of the form `1123` can be\nlisted as follows:\n\n```\nPERM\u003e (print-objects-of-spec (vector-to-word-spec #(1 1 2 3)))\n0 ==\u003e #(1 1 2 3) ==\u003e 0\n1 ==\u003e #(1 1 3 2) ==\u003e 1\n2 ==\u003e #(1 2 1 3) ==\u003e 2\n3 ==\u003e #(1 2 3 1) ==\u003e 3\n4 ==\u003e #(1 3 1 2) ==\u003e 4\n5 ==\u003e #(1 3 2 1) ==\u003e 5\n6 ==\u003e #(2 1 1 3) ==\u003e 6\n7 ==\u003e #(2 1 3 1) ==\u003e 7\n8 ==\u003e #(2 3 1 1) ==\u003e 8\n9 ==\u003e #(3 1 1 2) ==\u003e 9\n10 ==\u003e #(3 1 2 1) ==\u003e 10\n11 ==\u003e #(3 2 1 1) ==\u003e 11\n```\n\n\n## Ranking and Unranking Combinatorial Specifications\n\nEach combinatorial specification represents a finite space of `N \u003e 0`\nobjects. `N` is called the \"cardinality\" of the specification and can be\ncomputed with the `CARDINALITY` method.\n\n```\n\u003e (cardinality (make-perm-spec 3))\n6\n\u003e (cardinality (vector-to-word-spec #(1 1 2 3)))\n12\n```\n\nThe cardinality is computed only once for a combinatorial\nspecification and is then cached for fast access.\n\nObviously, every object in a particular finite combinatorial space can\nbe bijected to and from integers below the cardinality of that\nspace. `CL-PERMUTATION` provides fast and efficient mechanisms for\ncomputing one such bijection for each combinatorial\nspecification. Mapping from an object to an integer is called\n\"ranking\" and mapping from an integer back to an object is called\n\"unranking\".\n\nWhen a lexicographic ordering makes sense, there will be 1-to-1\ncorrespondence with the ordering on integers. In other words for\nobjects `X1` and `X2` and their ranks `R1` and `R2`, `X1 lex\u003c X2` iff `R1 \u003c R2`.\n\nThe method `UNRANK` takes a combinatorial specification and an integer,\nand maps it to the corresponding object representation (usually a\nvector). It takes an optional keyword argument `:SET` which acts as a\ndestination of the unranked object (for efficiency purposes).\n\nThe method `RANK` takes a combinatorial specification and an object\nproduced by `UNRANK` (again, usually a sensible vector) and returns the\ninteger (the \"rank\") of that object. `PRINT-OBJECTS-OF-SPEC`, as used\nabove, prints the rank of every object in a combinatorial space.\n\nOne can map over all objects and ranks by using `MAP-SPEC`, which takes\na binary function (rank and object) as well as a combinatorial\nspecification, and applies that function to each object and their\nrank.\n\n\n## Permutation Groups\n\nThere is initial support for permutation groups at the\nmoment. Permutation groups are represented by the structure\n`PERM-GROUP`.\n\nWe can create a permutation group from its generators via\n`GENERATE-PERM-GROUP`. A shorthand syntax is provided which, instead of\ntaking a list of `PERM` objects, takes a list of lists representing\nperms. This shorthand is `GROUP-FROM`. For example, the following two\nare the same group:\n\n```\nPERM\u003e (generate-perm-group (list (make-perm 1 3 2 4)\n                                 (make-perm 3 2 4 1)))\n#\u003cPERM-GROUP of 2 generators\u003e\nPERM\u003e (group-from '((1 3 2 4)\n                    (3 2 4 1)))\n#\u003cPERM-GROUP of 2 generators\u003e\n```\n\nWe can generate a permutation group from a list of cycles as well. The\nabove is equivalent to\n\n```\nPERM\u003e (group-from-cycles (list (list (make-cycle 2 3))\n                               (list (make-cycle 1 3 4)))\n                         4) \n#\u003cPERM-GROUP of 2 generators\u003e\n```\n\nOnce we have generated a group, we can do some operations on it.\n\nFor example, let's define the group for 3x3 Rubik's cubes. A cube has\nsix moves: we can turn the front, back, left, right, top, and\nbottom. Label each sticker with a number like so:\n\n```\n                     +--------------+\n                     |              |\n                     |  1    2    3 |\n                     |              |\n                     |  4   up    5 |\n                     |              |\n                     |  6    7    8 |\n                     |              |\n      +--------------+--------------+--------------+--------------+\n      |              |              |              |              |\n      |  9   10   11 | 17   18   19 | 25   26   27 | 33   34   35 |\n      |              |              |              |              |\n      | 12  left  13 | 20 front  21 | 28 right  29 | 36  back  37 |\n      |              |              |              |              |\n      | 14   15   16 | 22   23   24 | 30   31   32 | 38   39   40 |\n      |              |              |              |              |\n      +--------------+--------------+--------------+--------------+\n                     |              |\n                     | 41   42   43 |\n                     |              |\n                     | 44  down  45 |\n                     |              |\n                     | 46   47   48 |\n                     |              |\n                     +--------------+\n```\n\nEach turn corresponds to a permutation of stickers. I'll do the hard\nwork of specifying them:\n\n```\n(defparameter rubik-3x3\n  (group-from\n   '((3 5 8 2 7 1 4 6 33 34 35 12 13 14 15 16 9 10 11 20 21 22 23 24 17 \n      18 19 28 29 30 31 32 25 26 27 36 37 38 39 40 41 42 43 44 45 46 47 48)\n     (17 2 3 20 5 22 7 8 11 13 16 10 15 9 12 14 41 18 19 44 21 46 23 24 \n      25 26 27 28 29 30 31 32 33 34 6 36 4 38 39 1 40 42 43 37 45 35 47 48) \n     (1 2 3 4 5 25 28 30 9 10 8 12 7 14 15 6 19 21 24 18 23 17 20 22 43 \n      26 27 42 29 41 31 32 33 34 35 36 37 38 39 40 11 13 16 44 45 46 47 48) \n     (1 2 38 4 36 6 7 33 9 10 11 12 13 14 15 16 17 18 3 20 5 22 23 8 27 \n      29 32 26 31 25 28 30 48 34 35 45 37 43 39 40 41 42 19 44 21 46 47 24) \n     (14 12 9 4 5 6 7 8 46 10 11 47 13 48 15 16 17 18 19 20 21 22 23 24\n      25 26 1 28 2 30 31 3 35 37 40 34 39 33 36 38 41 42 43 44 45 32 29 27) \n     (1 2 3 4 5 6 7 8 9 10 11 12 13 22 23 24 17 18 19 20 21 30 31 32 25\n      26 27 28 29 38 39 40 33 34 35 36 37 14 15 16 43 45 48 42 47 41 44 46))))\n```\n\nNow we have our group:\n\n```\nPERM\u003e rubik-3x3\n#\u003cPERM-GROUP of 6 generators\u003e\n```\n\nLet's query the group's order:\n\n```\nPERM\u003e (group-order rubik-3x3)\n43252003274489856000\n```\n\nA lot of positions! Let's generate a random cube:\n\n```\nPERM\u003e (random-group-element rubik-3x3)\n#\u003cPERM 1 20 24 39 12 40 29 41 9 47 46 21 45 11 34 8 14 36 22 31 44 25 10 48\n       16 37 43 15 26 32 7 33 30 13 35 5 28 27 23 17 19 4 38 2 18 6 42 3\u003e\n```\n\nAnd as cycles...\n\n```\nPERM\u003e (to-cycles *)\n(#\u003cCYCLE (35)\u003e\n #\u003cCYCLE (30 32 33)\u003e\n #\u003cCYCLE (27 43 38)\u003e\n #\u003cCYCLE (9)\u003e\n #\u003cCYCLE (8 41 19 22 25 16)\u003e\n #\u003cCYCLE (6 40 17 14 11 46)\u003e\n #\u003cCYCLE (4 39 23 10 47 42)\u003e\n #\u003cCYCLE (3 24 48)\u003e\n #\u003cCYCLE (2 20 31 7 29 26 37 28 15 34 13 45 18 36 5 12 21 44)\u003e\n #\u003cCYCLE (1)\u003e)\n```\n\nLet's check if flipping an edge piece is valid:\n\n```\nPERM\u003e (group-element-p (from-cycles (list (make-cycle 7 18)) 48) rubik-3x3)\nNIL\n```\n\nNo it's not. How about four edge pieces?\n\n```\nPERM\u003e (group-element-p (from-cycles (list (make-cycle 7 18)\n                                          (make-cycle 2 34)\n                                          (make-cycle 4 10)\n                                          (make-cycle 5 26))\n                                    48)\n                       rubik-3x3)\nT\n```\n\nAs can be seen, the few operations we have are powerful in studying\nfinite groups.\n\n","funding_links":[],"categories":["Common Lisp","Expert Systems"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstylewarning%2Fcl-permutation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstylewarning%2Fcl-permutation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstylewarning%2Fcl-permutation/lists"}