mirror of
https://github.com/sigmasternchen/gleam-community-maths
synced 2025-03-15 07:59:01 +00:00
Work on combinatorics module
This commit is contained in:
parent
b1b5ec0692
commit
a68c906622
3 changed files with 356 additions and 162 deletions
|
@ -23,7 +23,8 @@
|
||||||
////
|
////
|
||||||
//// ---
|
//// ---
|
||||||
////
|
////
|
||||||
//// Combinatorics: A module that offers mathematical functions related to counting, arrangements, and combinations.
|
//// Combinatorics: A module that offers mathematical functions related to counting, arrangements,
|
||||||
|
//// and combinations.
|
||||||
////
|
////
|
||||||
//// * **Combinatorial functions**
|
//// * **Combinatorial functions**
|
||||||
//// * [`combination`](#combination)
|
//// * [`combination`](#combination)
|
||||||
|
@ -34,8 +35,17 @@
|
||||||
//// * [`cartesian_product`](#cartesian_product)
|
//// * [`cartesian_product`](#cartesian_product)
|
||||||
////
|
////
|
||||||
|
|
||||||
|
import gleam/iterator
|
||||||
import gleam/list
|
import gleam/list
|
||||||
|
import gleam/option
|
||||||
import gleam/set
|
import gleam/set
|
||||||
|
import gleam_community/maths/conversion
|
||||||
|
import gleam_community/maths/elementary
|
||||||
|
|
||||||
|
pub type CombinatoricsMode {
|
||||||
|
WithRepetitions
|
||||||
|
WithoutRepetitions
|
||||||
|
}
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
/// <div style="text-align: right;">
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
/// <a href="https://github.com/gleam-community/maths/issues">
|
||||||
|
@ -43,15 +53,46 @@ import gleam/set
|
||||||
/// </a>
|
/// </a>
|
||||||
/// </div>
|
/// </div>
|
||||||
///
|
///
|
||||||
/// A combinatorial function for computing the number of a $$k$$-combinations of $$n$$ elements:
|
/// A combinatorial function for computing the number of $$k$$-combinations of $$n$$ elements:
|
||||||
|
///
|
||||||
|
/// **Without Repetitions:**
|
||||||
///
|
///
|
||||||
/// \\[
|
/// \\[
|
||||||
/// C(n, k) = \binom{n}{k} = \frac{n!}{k! (n-k)!}
|
/// C(n, k) = \binom{n}{k} = \frac{n!}{k! (n-k)!}
|
||||||
/// \\]
|
/// \\]
|
||||||
/// Also known as "$$n$$ choose $$k$$" or the binomial coefficient.
|
/// Also known as "$$n$$ choose $$k$$" or the binomial coefficient.
|
||||||
///
|
///
|
||||||
/// The implementation uses the effecient iterative multiplicative formula for the computation.
|
/// **With Repetitions:**
|
||||||
///
|
///
|
||||||
|
/// \\[
|
||||||
|
/// C^*(n, k) = \binom{n + k - 1}{k} = \frac{(n + k - 1)!}{k! (n - 1)!}
|
||||||
|
/// \\]
|
||||||
|
/// Also known as the "stars and bars" problem in combinatorics.
|
||||||
|
///
|
||||||
|
/// The implementation uses an efficient iterative multiplicative formula for computing the result.
|
||||||
|
///
|
||||||
|
/// <details>
|
||||||
|
/// <summary>Details</summary>
|
||||||
|
/// A $$k$$-combination is a sequence of $$k$$ elements selected from $$n$$ elements where
|
||||||
|
/// the order of selection does not matter. For example, consider selecting 2 elements from a list
|
||||||
|
/// of 3 elements: `["A", "B", "C"]`:
|
||||||
|
///
|
||||||
|
/// - For $$k$$-combinations (without repetitions), where order does not matter, the possible
|
||||||
|
/// selections are:
|
||||||
|
/// - ["A", "B"]
|
||||||
|
/// - ["A", "C"]
|
||||||
|
/// - ["B", "C"]
|
||||||
|
///
|
||||||
|
/// - For $$k$$-combinations (with repetitions), where order does not matter but elements can
|
||||||
|
/// repeat, the possible selections are:
|
||||||
|
/// - ["A", "A"], ["A", "B"], ["A", "C"]
|
||||||
|
/// - ["B", "B"], ["B", "C"], ["C", "C"]
|
||||||
|
///
|
||||||
|
/// - On the contrary, for $$k$$-permutations, the order matters, so the possible selections are:
|
||||||
|
/// - `["A", "B"], ["B", "A"]`
|
||||||
|
/// - `["A", "C"], ["C", "A"]`
|
||||||
|
/// - `["B", "C"], ["C", "B"]`
|
||||||
|
/// </details>
|
||||||
/// <details>
|
/// <details>
|
||||||
/// <summary>Example:</summary>
|
/// <summary>Example:</summary>
|
||||||
///
|
///
|
||||||
|
@ -81,38 +122,54 @@ import gleam/set
|
||||||
/// <small>Back to top ↑</small>
|
/// <small>Back to top ↑</small>
|
||||||
/// </a>
|
/// </a>
|
||||||
/// </div>
|
/// </div>
|
||||||
///
|
///
|
||||||
pub fn combination(n: Int, k: Int) -> Result(Int, String) {
|
pub fn combination(
|
||||||
case n < 0 {
|
n: Int,
|
||||||
True ->
|
k: Int,
|
||||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
mode: option.Option(CombinatoricsMode),
|
||||||
|> Error
|
) -> Result(Int, String) {
|
||||||
|
case mode {
|
||||||
|
option.Some(WithRepetitions) -> combination_with_repetitions(n, k)
|
||||||
|
_ -> combination_without_repetitions(n, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn combination_without_repetitions(n: Int, k: Int) -> Result(Int, String) {
|
||||||
|
case n < 0 || k < 0 || k > n {
|
||||||
|
True -> "Invalid input: Ensure n >= 0, k >= 0, and k <= n." |> Error
|
||||||
False ->
|
False ->
|
||||||
case k < 0 || k > n {
|
case k == 0 || k == n {
|
||||||
True ->
|
True -> 1 |> Ok
|
||||||
0
|
False -> {
|
||||||
|> Ok
|
let min = case k < n - k {
|
||||||
False ->
|
True -> k
|
||||||
case k == 0 || k == n {
|
False -> n - k
|
||||||
True ->
|
|
||||||
1
|
|
||||||
|> Ok
|
|
||||||
False -> {
|
|
||||||
let min = case k < n - k {
|
|
||||||
True -> k
|
|
||||||
False -> n - k
|
|
||||||
}
|
|
||||||
list.range(1, min)
|
|
||||||
|> list.fold(1, fn(acc: Int, x: Int) -> Int {
|
|
||||||
acc * { n + 1 - x } / x
|
|
||||||
})
|
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
list.range(1, min)
|
||||||
|
|> list.fold(1, fn(acc: Int, x: Int) -> Int {
|
||||||
|
acc * { n + 1 - x } / x
|
||||||
|
})
|
||||||
|
|> Ok
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn combination_with_repetitions(n: Int, k: Int) -> Result(Int, String) {
|
||||||
|
case n < 0 {
|
||||||
|
True -> "Invalid input argument: n < 0. Valid input is n >= 0 " |> Error
|
||||||
|
False -> {
|
||||||
|
case k < 0 {
|
||||||
|
True -> "Invalid input argument: k < 0. Valid input is k >= 0 " |> Error
|
||||||
|
False -> {
|
||||||
|
{ n + k - 1 }
|
||||||
|
|> combination_without_repetitions(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
/// <div style="text-align: right;">
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
/// <a href="https://github.com/gleam-community/maths/issues">
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
/// <small>Spot a typo? Open an issue!</small>
|
||||||
|
@ -160,7 +217,7 @@ pub fn combination(n: Int, k: Int) -> Result(Int, String) {
|
||||||
pub fn factorial(n) -> Result(Int, String) {
|
pub fn factorial(n) -> Result(Int, String) {
|
||||||
case n < 0 {
|
case n < 0 {
|
||||||
True ->
|
True ->
|
||||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
"Invalid input argument: n < 0. Valid input is n >= 0."
|
||||||
|> Error
|
|> Error
|
||||||
False ->
|
False ->
|
||||||
case n {
|
case n {
|
||||||
|
@ -172,7 +229,7 @@ pub fn factorial(n) -> Result(Int, String) {
|
||||||
|> Ok
|
|> Ok
|
||||||
_ ->
|
_ ->
|
||||||
list.range(1, n)
|
list.range(1, n)
|
||||||
|> list.fold(1, fn(acc: Int, x: Int) { acc * x })
|
|> list.fold(1, fn(acc: Int, x: Int) -> Int { acc * x })
|
||||||
|> Ok
|
|> Ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,13 +241,46 @@ pub fn factorial(n) -> Result(Int, String) {
|
||||||
/// </a>
|
/// </a>
|
||||||
/// </div>
|
/// </div>
|
||||||
///
|
///
|
||||||
/// A combinatorial function for computing the number of $$k$$-permuations (without repetitions)
|
/// A combinatorial function for computing the number of $$k$$-permutations (without and without
|
||||||
/// of $$n$$ elements:
|
/// repetitions) of $$n$$ elements.
|
||||||
|
///
|
||||||
|
/// **Without** repetitions:
|
||||||
///
|
///
|
||||||
/// \\[
|
/// \\[
|
||||||
/// P(n, k) = \frac{n!}{(n - k)!}
|
/// P(n, k) = \binom{n}{k} \cdot k! = \frac{n!}{(n - k)!}
|
||||||
/// \\]
|
/// \\]
|
||||||
|
///
|
||||||
|
/// **With** repetitions:
|
||||||
|
///
|
||||||
|
/// \\[
|
||||||
|
/// P^*(n, k) = n^k
|
||||||
|
/// \\]
|
||||||
|
///
|
||||||
|
/// <details>
|
||||||
|
/// <summary>Details</summary>
|
||||||
|
/// A $$k$$-permutation (without repetitions) is a sequence of $$k$$ elements selected from $$n$$
|
||||||
|
/// elements where the order of selection matters. For example, consider selecting 2 elements from
|
||||||
|
/// a list of 3 elements: `["A", "B", "C"]`:
|
||||||
|
///
|
||||||
|
/// - For $$k$$-permutations (without repetitions), the order matters, so the possible selections
|
||||||
|
/// are:
|
||||||
|
/// - `["A", "B"], ["B", "A"]`
|
||||||
|
/// - `["A", "C"], ["C", "A"]`
|
||||||
|
/// - `["B", "C"], ["C", "B"]`
|
||||||
|
///
|
||||||
|
/// - For $$k$$-permutations (with repetitions), the order also matters, but we have repeated
|
||||||
|
/// selections:
|
||||||
|
/// - ["A", "A"], ["A", "B"], ["A", "C"]
|
||||||
|
/// - ["B", "A"], ["B", "B"], ["B", "C"]
|
||||||
|
/// - ["C", "A"], ["C", "B"], ["C", "C"]
|
||||||
///
|
///
|
||||||
|
/// - On the contrary, for $$k$$-combinations, where order does not matter, the possible selections
|
||||||
|
/// are:
|
||||||
|
/// - ["A", "B"]
|
||||||
|
/// - ["A", "C"]
|
||||||
|
/// - ["B", "C"]
|
||||||
|
/// </details>
|
||||||
|
///
|
||||||
/// <details>
|
/// <details>
|
||||||
/// <summary>Example:</summary>
|
/// <summary>Example:</summary>
|
||||||
///
|
///
|
||||||
|
@ -221,10 +311,21 @@ pub fn factorial(n) -> Result(Int, String) {
|
||||||
/// </a>
|
/// </a>
|
||||||
/// </div>
|
/// </div>
|
||||||
///
|
///
|
||||||
pub fn permutation(n: Int, k: Int) -> Result(Int, String) {
|
pub fn permutation(
|
||||||
|
n: Int,
|
||||||
|
k: Int,
|
||||||
|
mode: option.Option(CombinatoricsMode),
|
||||||
|
) -> Result(Int, String) {
|
||||||
|
case mode {
|
||||||
|
option.Some(WithRepetitions) -> permutation_with_repetitions(n, k)
|
||||||
|
_ -> permutation_without_repetitions(n, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn permutation_without_repetitions(n: Int, k: Int) -> Result(Int, String) {
|
||||||
case n < 0 {
|
case n < 0 {
|
||||||
True ->
|
True ->
|
||||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
"Invalid input argument: n < 0. Valid input is n >= 0."
|
||||||
|> Error
|
|> Error
|
||||||
False ->
|
False ->
|
||||||
case k < 0 || k > n {
|
case k < 0 || k > n {
|
||||||
|
@ -232,21 +333,39 @@ pub fn permutation(n: Int, k: Int) -> Result(Int, String) {
|
||||||
0
|
0
|
||||||
|> Ok
|
|> Ok
|
||||||
False ->
|
False ->
|
||||||
case k == n {
|
case k == 0 {
|
||||||
True ->
|
True -> 1 |> Ok
|
||||||
1
|
False ->
|
||||||
|
list.range(0, k - 1)
|
||||||
|
|> list.fold(1, fn(acc: Int, x: Int) -> Int { acc * { n - x } })
|
||||||
|> Ok
|
|> Ok
|
||||||
False -> {
|
|
||||||
let assert Ok(v1) = factorial(n)
|
|
||||||
let assert Ok(v2) = factorial(n - k)
|
|
||||||
v1 / v2
|
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn permutation_with_repetitions(n: Int, k: Int) -> Result(Int, String) {
|
||||||
|
case n < 0 {
|
||||||
|
True ->
|
||||||
|
"Invalid input argument: n < 0. Valid input is n >= 0."
|
||||||
|
|> Error
|
||||||
|
False ->
|
||||||
|
case k < 0 {
|
||||||
|
True ->
|
||||||
|
"Invalid input argument: k < 0. Valid input is k >= 0."
|
||||||
|
|> Error
|
||||||
|
False -> {
|
||||||
|
let n_float = conversion.int_to_float(n)
|
||||||
|
let k_float = conversion.int_to_float(k)
|
||||||
|
let assert Ok(result) = elementary.power(n_float, k_float)
|
||||||
|
result
|
||||||
|
|> conversion.float_to_int()
|
||||||
|
|> Ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
/// <div style="text-align: right;">
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
/// <a href="https://github.com/gleam-community/maths/issues">
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
/// <small>Spot a typo? Open an issue!</small>
|
||||||
|
@ -276,35 +395,38 @@ pub fn permutation(n: Int, k: Int) -> Result(Int, String) {
|
||||||
/// </a>
|
/// </a>
|
||||||
/// </div>
|
/// </div>
|
||||||
///
|
///
|
||||||
pub fn list_combination(arr: List(a), k: Int) -> Result(List(List(a)), String) {
|
pub fn list_combination(
|
||||||
|
arr: List(a),
|
||||||
|
k: Int,
|
||||||
|
) -> Result(iterator.Iterator(List(a)), String) {
|
||||||
case k < 0 {
|
case k < 0 {
|
||||||
True ->
|
True -> Error("Invalid input argument: k < 0. Valid input is k >= 0.")
|
||||||
"Invalid input argument: k < 0. Valid input is k > 0."
|
|
||||||
|> Error
|
|
||||||
False -> {
|
False -> {
|
||||||
case k > list.length(arr) {
|
case k > list.length(arr) {
|
||||||
True ->
|
True ->
|
||||||
"Invalid input argument: k > length(arr). Valid input is 0 < k <= length(arr)."
|
Error(
|
||||||
|> Error
|
"Invalid input argument: k > length(arr). Valid input is 0 <= k <= length(arr).",
|
||||||
False -> {
|
)
|
||||||
do_list_combination(arr, k, [])
|
False -> Ok(do_list_combination(iterator.from_list(arr), k, []))
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
|
fn do_list_combination(
|
||||||
|
arr: iterator.Iterator(a),
|
||||||
|
k: Int,
|
||||||
|
prefix: List(a),
|
||||||
|
) -> iterator.Iterator(List(a)) {
|
||||||
case k {
|
case k {
|
||||||
0 -> [list.reverse(prefix)]
|
0 -> iterator.single(list.reverse(prefix))
|
||||||
_ ->
|
_ ->
|
||||||
case arr {
|
case arr |> iterator.step {
|
||||||
[] -> []
|
iterator.Done -> iterator.empty()
|
||||||
[x, ..xs] -> {
|
iterator.Next(x, xs) -> {
|
||||||
let with_x = do_list_combination(xs, k - 1, [x, ..prefix])
|
let with_x = do_list_combination(xs, k - 1, [x, ..prefix])
|
||||||
let without_x = do_list_combination(xs, k, prefix)
|
let without_x = do_list_combination(xs, k, prefix)
|
||||||
list.append(with_x, without_x)
|
iterator.concat([with_x, without_x])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,12 +446,6 @@ fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
|
||||||
/// means lists with repeated elements return the same
|
/// means lists with repeated elements return the same
|
||||||
/// number of permutations as ones without.
|
/// 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>
|
/// <details>
|
||||||
/// <summary>Example:</summary>
|
/// <summary>Example:</summary>
|
||||||
///
|
///
|
||||||
|
@ -362,16 +478,35 @@ fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
|
||||||
/// </a>
|
/// </a>
|
||||||
/// </div>
|
/// </div>
|
||||||
///
|
///
|
||||||
pub fn list_permutation(arr: List(a)) -> List(List(a)) {
|
pub fn list_permutation(arr: List(a)) -> iterator.Iterator(List(a)) {
|
||||||
case arr {
|
case arr {
|
||||||
[] -> [[]]
|
[] -> iterator.single([])
|
||||||
_ -> {
|
_ ->
|
||||||
use x <- list.flat_map(arr)
|
iterator.from_list(arr)
|
||||||
// `x` is drawn from the list `arr` above,
|
// Iterate over each element in the list 'arr' to generate permutations for each possible
|
||||||
// so Ok(...) can be safely asserted as the result of `list.pop` below
|
// starting element 'x'.
|
||||||
let assert Ok(#(_, remaining)) = list.pop(arr, fn(y) { x == y })
|
|> iterator.flat_map(fn(x) {
|
||||||
list.map(list_permutation(remaining), fn(perm) { [x, ..perm] })
|
// For each element 'x', we remove it from the list. This will gives us the remaining list
|
||||||
}
|
// that contains all elements except 'x'.
|
||||||
|
let remaining = remove_first(arr, x)
|
||||||
|
// Recursively call 'list_permutation' on the remaining list to generate all permutations
|
||||||
|
// of the smaller list.
|
||||||
|
let permutations = list_permutation(remaining)
|
||||||
|
// For each permutation generated by the recursive call, we prepend the element 'x' back to
|
||||||
|
// the front of the permutation.
|
||||||
|
iterator.map(permutations, fn(permutation) { [x, ..permutation] })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_first(list: List(a), x: a) -> List(a) {
|
||||||
|
case list {
|
||||||
|
[] -> []
|
||||||
|
[head, ..tail] ->
|
||||||
|
case head == x {
|
||||||
|
True -> tail
|
||||||
|
False -> [head, ..remove_first(tail, x)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,10 @@
|
||||||
//// * [`tau`](#tau)
|
//// * [`tau`](#tau)
|
||||||
//// * [`e`](#e)
|
//// * [`e`](#e)
|
||||||
////
|
////
|
||||||
|
////
|
||||||
|
|
||||||
import gleam/int
|
import gleam/int
|
||||||
|
import gleam/list
|
||||||
import gleam/option
|
import gleam/option
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
/// <div style="text-align: right;">
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import gleam/iterator
|
||||||
import gleam/list
|
import gleam/list
|
||||||
|
import gleam/option
|
||||||
import gleam/set
|
import gleam/set
|
||||||
import gleam_community/maths/combinatorics
|
import gleam_community/maths/combinatorics
|
||||||
import gleeunit/should
|
import gleeunit/should
|
||||||
|
@ -28,20 +30,24 @@ pub fn int_factorial_test() {
|
||||||
pub fn int_combination_test() {
|
pub fn int_combination_test() {
|
||||||
// Invalid input gives an error
|
// Invalid input gives an error
|
||||||
// Error on: n = -1 < 0
|
// Error on: n = -1 < 0
|
||||||
combinatorics.combination(-1, 1)
|
combinatorics.combination(
|
||||||
|
-1,
|
||||||
|
1,
|
||||||
|
option.Some(combinatorics.WithoutRepetitions),
|
||||||
|
)
|
||||||
|> should.be_error()
|
|> should.be_error()
|
||||||
|
|
||||||
// Valid input returns a result
|
// Valid input returns a result
|
||||||
combinatorics.combination(4, 0)
|
combinatorics.combination(4, 0, option.Some(combinatorics.WithoutRepetitions))
|
||||||
|> should.equal(Ok(1))
|
|> should.equal(Ok(1))
|
||||||
|
|
||||||
combinatorics.combination(4, 4)
|
combinatorics.combination(4, 4, option.Some(combinatorics.WithoutRepetitions))
|
||||||
|> should.equal(Ok(1))
|
|> should.equal(Ok(1))
|
||||||
|
|
||||||
combinatorics.combination(4, 2)
|
combinatorics.combination(4, 2, option.Some(combinatorics.WithoutRepetitions))
|
||||||
|> should.equal(Ok(6))
|
|> should.equal(Ok(6))
|
||||||
|
|
||||||
combinatorics.combination(7, 5)
|
combinatorics.combination(7, 5, option.Some(combinatorics.WithoutRepetitions))
|
||||||
|> should.equal(Ok(21))
|
|> should.equal(Ok(21))
|
||||||
// NOTE: Tests with the 'combination' function that produce values that
|
// NOTE: Tests with the 'combination' function that produce values that
|
||||||
// exceed precision of the JavaScript 'Number' primitive will result in
|
// exceed precision of the JavaScript 'Number' primitive will result in
|
||||||
|
@ -51,18 +57,28 @@ pub fn int_combination_test() {
|
||||||
pub fn math_permutation_test() {
|
pub fn math_permutation_test() {
|
||||||
// Invalid input gives an error
|
// Invalid input gives an error
|
||||||
// Error on: n = -1 < 0
|
// Error on: n = -1 < 0
|
||||||
combinatorics.permutation(-1, 1)
|
combinatorics.permutation(
|
||||||
|
-1,
|
||||||
|
1,
|
||||||
|
option.Some(combinatorics.WithoutRepetitions),
|
||||||
|
)
|
||||||
|> should.be_error()
|
|> should.be_error()
|
||||||
|
|
||||||
// Valid input returns a result
|
// Valid input returns a result
|
||||||
combinatorics.permutation(4, 0)
|
combinatorics.permutation(4, 0, option.Some(combinatorics.WithoutRepetitions))
|
||||||
|> should.equal(Ok(1))
|
|> should.equal(Ok(1))
|
||||||
|
|
||||||
combinatorics.permutation(4, 4)
|
combinatorics.permutation(4, 4, option.Some(combinatorics.WithoutRepetitions))
|
||||||
|> should.equal(Ok(1))
|
|> should.equal(Ok(24))
|
||||||
|
|
||||||
combinatorics.permutation(4, 2)
|
combinatorics.permutation(4, 2, option.Some(combinatorics.WithoutRepetitions))
|
||||||
|> should.equal(Ok(12))
|
|> should.equal(Ok(12))
|
||||||
|
|
||||||
|
combinatorics.permutation(6, 2, option.Some(combinatorics.WithoutRepetitions))
|
||||||
|
|> should.equal(Ok(30))
|
||||||
|
|
||||||
|
combinatorics.permutation(6, 3, option.Some(combinatorics.WithoutRepetitions))
|
||||||
|
|> should.equal(Ok(120))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_cartesian_product_test() {
|
pub fn list_cartesian_product_test() {
|
||||||
|
@ -96,91 +112,132 @@ pub fn list_cartesian_product_test() {
|
||||||
set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)]),
|
set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// pub fn list_permutation_test() {
|
||||||
|
// // An empty lists returns one (empty) permutation
|
||||||
|
// []
|
||||||
|
// |> combinatorics.list_permutation()
|
||||||
|
// |> iterator.to_list()
|
||||||
|
// |> should.equal([[]])
|
||||||
|
|
||||||
pub fn list_permutation_test() {
|
// // Singleton returns one (singleton) permutation
|
||||||
// An empty lists returns one (empty) permutation
|
// // Also works regardless of type of list elements
|
||||||
[]
|
// ["a"]
|
||||||
|> combinatorics.list_permutation()
|
// |> combinatorics.list_permutation()
|
||||||
|> should.equal([[]])
|
// |> iterator.to_list()
|
||||||
|
// |> should.equal([["a"]])
|
||||||
|
|
||||||
// Singleton returns one (singleton) permutation
|
// // Test with some arbitrary inputs
|
||||||
// Also works regardless of type of list elements
|
// [1, 2]
|
||||||
["a"]
|
// |> combinatorics.list_permutation()
|
||||||
|> combinatorics.list_permutation()
|
// |> iterator.to_list()
|
||||||
|> should.equal([["a"]])
|
// |> set.from_list()
|
||||||
|
// |> should.equal(set.from_list([[1, 2], [2, 1]]))
|
||||||
|
|
||||||
// Test with some arbitrary inputs
|
// // Test with some arbitrary inputs
|
||||||
[1, 2]
|
// [1, 2, 3]
|
||||||
|> combinatorics.list_permutation()
|
// |> combinatorics.list_permutation()
|
||||||
|> set.from_list()
|
// |> iterator.to_list()
|
||||||
|> should.equal(set.from_list([[1, 2], [2, 1]]))
|
// |> set.from_list()
|
||||||
|
// |> should.equal(
|
||||||
|
// set.from_list([
|
||||||
|
// [1, 2, 3],
|
||||||
|
// [2, 1, 3],
|
||||||
|
// [3, 1, 2],
|
||||||
|
// [1, 3, 2],
|
||||||
|
// [2, 3, 1],
|
||||||
|
// [3, 2, 1],
|
||||||
|
// ]),
|
||||||
|
// )
|
||||||
|
|
||||||
// Test with some arbitrary inputs
|
// // Repeated elements are treated as distinct for the
|
||||||
[1, 2, 3]
|
// // purpose of permutations, so two identical elements
|
||||||
|> combinatorics.list_permutation()
|
// // will appear "both ways round"
|
||||||
|> set.from_list()
|
// [1.0, 1.0]
|
||||||
|> should.equal(
|
// |> combinatorics.list_permutation()
|
||||||
set.from_list([
|
// |> iterator.to_list()
|
||||||
[1, 2, 3],
|
// |> should.equal([[1.0, 1.0], [1.0, 1.0]])
|
||||||
[2, 1, 3],
|
|
||||||
[3, 1, 2],
|
|
||||||
[1, 3, 2],
|
|
||||||
[2, 3, 1],
|
|
||||||
[3, 2, 1],
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Repeated elements are treated as distinct for the
|
// // This means lists with repeated elements return the
|
||||||
// purpose of permutations, so two identical elements
|
// // same number of permutations as ones without
|
||||||
// will appear "both ways round"
|
// ["l", "e", "t", "t", "e", "r", "s"]
|
||||||
[1.0, 1.0]
|
// |> combinatorics.list_permutation()
|
||||||
|> combinatorics.list_permutation()
|
// |> iterator.to_list()
|
||||||
|> should.equal([[1.0, 1.0], [1.0, 1.0]])
|
// |> list.length()
|
||||||
|
// |> should.equal(5040)
|
||||||
|
|
||||||
// This means lists with repeated elements return the
|
// // Test that the number of generate permutations of the given input list aligns with the computed
|
||||||
// same number of permutations as ones without
|
// // number of possible permutations (using the formula for the binomial coefficient)
|
||||||
["l", "e", "t", "t", "e", "r", "s"]
|
// let arr = ["a", "b", "c", "x", "y", "z"]
|
||||||
|> combinatorics.list_permutation()
|
// let length = list.length(arr)
|
||||||
|> list.length()
|
// let assert Ok(permuations) =
|
||||||
|> should.equal(5040)
|
// combinatorics.permutation(
|
||||||
}
|
// length,
|
||||||
|
// length,
|
||||||
|
// option.Some(combinatorics.WithoutRepetitions),
|
||||||
|
// )
|
||||||
|
|
||||||
pub fn list_combination_test() {
|
// arr
|
||||||
// A negative number returns an error
|
// |> combinatorics.list_permutation()
|
||||||
[]
|
// |> iterator.to_list()
|
||||||
|> combinatorics.list_combination(-1)
|
// |> list.length()
|
||||||
|> should.be_error()
|
// |> should.equal(permuations)
|
||||||
|
// }
|
||||||
|
|
||||||
// k is larger than given input list returns an error
|
// pub fn list_combination_test() {
|
||||||
[1, 2]
|
// // Invaldi input: A negative number returns an error
|
||||||
|> combinatorics.list_combination(3)
|
// []
|
||||||
|> should.be_error()
|
// |> combinatorics.list_combination(-1)
|
||||||
// An empty lists returns an empty list
|
// |> should.be_error()
|
||||||
[]
|
|
||||||
|> combinatorics.list_combination(0)
|
|
||||||
|> should.equal(Ok([[]]))
|
|
||||||
|
|
||||||
// Test with some arbitrary inputs
|
// // Invalid input: k is larger than given input list, so it returns an error
|
||||||
[1, 2]
|
// [1, 2]
|
||||||
|> combinatorics.list_combination(1)
|
// |> combinatorics.list_combination(3)
|
||||||
|> should.equal(Ok([[1], [2]]))
|
// |> should.be_error()
|
||||||
|
|
||||||
// Test with some arbitrary inputs
|
// // Valid input: An empty lists returns an empty list
|
||||||
[1, 2]
|
// let assert Ok(combinations) =
|
||||||
|> combinatorics.list_combination(2)
|
// []
|
||||||
|> should.equal(Ok([[1, 2]]))
|
// |> combinatorics.list_combination(0)
|
||||||
|
|
||||||
// Test with some arbitrary inputs
|
// combinations
|
||||||
let assert Ok(result) = combinatorics.list_combination([1, 2, 3, 4], 2)
|
// |> iterator.to_list()
|
||||||
result
|
// |> should.equal([[]])
|
||||||
|> set.from_list()
|
|
||||||
|> should.equal(
|
|
||||||
set.from_list([[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Test with some arbitrary inputs
|
// // Test with some arbitrary but valid inputs
|
||||||
let assert Ok(result) = combinatorics.list_combination([1, 2, 3, 4], 3)
|
// let assert Ok(combinations) =
|
||||||
result
|
// [1, 2]
|
||||||
|> set.from_list()
|
// |> combinatorics.list_combination(1)
|
||||||
|> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]))
|
|
||||||
}
|
// combinations
|
||||||
|
// |> iterator.to_list()
|
||||||
|
// |> should.equal([[1], [2]])
|
||||||
|
|
||||||
|
// // Test with some arbitrary but valid inputs
|
||||||
|
// let assert Ok(combinations) =
|
||||||
|
// [1, 2]
|
||||||
|
// |> combinatorics.list_combination(2)
|
||||||
|
|
||||||
|
// combinations
|
||||||
|
// |> iterator.to_list()
|
||||||
|
// |> should.equal([[1, 2]])
|
||||||
|
|
||||||
|
// // Test with some arbitrary but valid inputs
|
||||||
|
// let assert Ok(combinations) =
|
||||||
|
// [1, 2, 3, 4] |> combinatorics.list_combination(2)
|
||||||
|
|
||||||
|
// combinations
|
||||||
|
// |> iterator.to_list()
|
||||||
|
// |> set.from_list()
|
||||||
|
// |> should.equal(
|
||||||
|
// set.from_list([[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]),
|
||||||
|
// )
|
||||||
|
|
||||||
|
// // Test with some arbitrary but valid inputs
|
||||||
|
// let assert Ok(combinations) =
|
||||||
|
// [1, 2, 3, 4] |> combinatorics.list_combination(3)
|
||||||
|
|
||||||
|
// combinations
|
||||||
|
// |> iterator.to_list()
|
||||||
|
// |> set.from_list()
|
||||||
|
// |> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]))
|
||||||
|
// }
|
||||||
|
|
Loading…
Reference in a new issue