mirror of
https://github.com/sigmasternchen/gleam-community-maths
synced 2025-03-15 07:59:01 +00:00
Merge pull request #9 from DanielSherlock/permutations
Fixed and tidied `combinatorics.list_permutation` function
This commit is contained in:
commit
ad11b966a9
2 changed files with 47 additions and 23 deletions
|
@ -319,6 +319,18 @@ fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
|
|||
///
|
||||
/// Generate all permutations of a given list.
|
||||
///
|
||||
/// Repeated elements are treated as distinct for the
|
||||
/// purpose of permutations, so two identical elements
|
||||
/// for example will appear "both ways round". This
|
||||
/// means lists with repeated elements return the same
|
||||
/// number of permutations as ones without.
|
||||
///
|
||||
/// N.B. The output of this function is a list of size
|
||||
/// factorial in the size of the input list. Caution is
|
||||
/// advised on input lists longer than ~11 elements, which
|
||||
/// may cause the VM to use unholy amounts of memory for
|
||||
/// the output.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
|
@ -338,6 +350,10 @@ fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
|
|||
/// [2, 3, 1],
|
||||
/// [3, 2, 1],
|
||||
/// ]))
|
||||
///
|
||||
/// [1.0, 1.0]
|
||||
/// |> combinatorics.list_permutation()
|
||||
/// |> should.equal([[1.0, 1.0], [1.0, 1.0]])
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
|
@ -350,30 +366,16 @@ fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
|
|||
pub fn list_permutation(arr: List(a)) -> List(List(a)) {
|
||||
case arr {
|
||||
[] -> [[]]
|
||||
_ ->
|
||||
flat_map(
|
||||
arr,
|
||||
fn(x) {
|
||||
let remaining = list.filter(arr, fn(y) { x != y })
|
||||
list.map(list_permutation(remaining), fn(perm) { [x, ..perm] })
|
||||
},
|
||||
)
|
||||
_ -> {
|
||||
use x <- list.flat_map(arr)
|
||||
// `x` is drawn from the list `arr` above,
|
||||
// so Ok(...) can be safely asserted as the result of `list.pop` below
|
||||
let assert Ok(#(_, remaining)) = list.pop(arr, fn(y) { x == y })
|
||||
list.map(list_permutation(remaining), fn(perm) { [x, ..perm] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Flat map function
|
||||
fn flat_map(list: List(a), f: fn(a) -> List(b)) -> List(b) {
|
||||
list
|
||||
|> list.map(f)
|
||||
|> concat()
|
||||
}
|
||||
|
||||
/// Concatenate a list of lists
|
||||
fn concat(lists: List(List(a))) -> List(a) {
|
||||
lists
|
||||
|> list.fold([], list.append)
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import gleam_community/maths/combinatorics
|
||||
import gleam/set
|
||||
import gleam/list
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
|
@ -103,15 +104,22 @@ pub fn list_cartesian_product_test() {
|
|||
}
|
||||
|
||||
pub fn list_permutation_test() {
|
||||
// An empty lists returns an empty list
|
||||
// An empty lists returns one (empty) permutation
|
||||
[]
|
||||
|> combinatorics.list_permutation()
|
||||
|> should.equal([[]])
|
||||
|
||||
// Singleton returns one (singleton) permutation
|
||||
// Also works regardless of type of list elements
|
||||
["a"]
|
||||
|> combinatorics.list_permutation()
|
||||
|> should.equal([["a"]])
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
[1, 2]
|
||||
|> combinatorics.list_permutation()
|
||||
|> should.equal([[1, 2], [2, 1]])
|
||||
|> set.from_list()
|
||||
|> should.equal(set.from_list([[1, 2], [2, 1]]))
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
[1, 2, 3]
|
||||
|
@ -125,6 +133,20 @@ pub fn list_permutation_test() {
|
|||
[2, 3, 1],
|
||||
[3, 2, 1],
|
||||
]))
|
||||
|
||||
// Repeated elements are treated as distinct for the
|
||||
// purpose of permutations, so two identical elements
|
||||
// will appear "both ways round"
|
||||
[1.0, 1.0]
|
||||
|> combinatorics.list_permutation()
|
||||
|> should.equal([[1.0, 1.0], [1.0, 1.0]])
|
||||
|
||||
// This means lists with repeated elements return the
|
||||
// same number of permutations as ones without
|
||||
["l", "e", "t", "t", "e", "r", "s"]
|
||||
|> combinatorics.list_permutation()
|
||||
|> list.length()
|
||||
|> should.equal(5040)
|
||||
}
|
||||
|
||||
pub fn list_combination_test() {
|
||||
|
|
Loading…
Reference in a new issue