Number of combinations/permutations

Viewed 868

How do I find the number of permutations or combinations in rust?

E.g. C(10,6) = 210

I can't find this function in the standard library, nor can I find the factorial operator there (which would suffice).

2 Answers

Building off @vallentin's answer, there are many optimizations that could be made. Let's use the same factorial function (for now):

fn factorial(n: u64) -> u64 {
    (1..=n).product()
}

For count_permutations, n! / (n - r)! is actually just the product of all numbers between n - r + 1 and n (inclusive), so we don't even need to compute 2 factorials (which could overflow and involve computing the product of overlapping numbers):

fn count_permutations(n: u64, r: u64) -> u64 {
    (n - r + 1..=n).product()
}

We can do a similar optimization for count_combinations:

fn count_combinations(n: u64, r: u64) -> u64 {
    (n - r + 1..=n).product::<u64>() / factorial(r)
}

count_permutations is pretty much completely optimized, being both fast and correct (if the result of count_permutations can fit in a u64 then it should never overflow).

count_combinations still has a couple flaws, namely that since it's computing a product then dividing, its result could fit in a u64, but the function will still overflow. You can make it pretty close to non-overflowing with alternating multiplication and division:

fn count_combinations(n: u64, r: u64) -> u64 {
    if r > n {
        0
    } else {
        (1..=r).fold(1, |acc, val| acc * (n - val + 1) / val)
    }
}

Putting it all together:

fn count_combinations(n: u64, r: u64) -> u64 {
    if r > n {
        0
    } else {
        (1..=r).fold(1, |acc, val| acc * (n - val + 1) / val)
    }
}

fn count_permutations(n: u64, r: u64) -> u64 {
    (n - r + 1..=n).product()
}

fn main() {
    println!("{}", count_combinations(10, 6));
    println!("{}", count_permutations(10, 6));
}

Note that there are some micro-optimizations that you can do, namely going to r.min(n - r) instead of r for count_combinations, since count_combinations(n, r) == count_combinations(n, n - r), and going to the smaller of the two will shrink the loop size:

fn count_combinations(n: u64, r: u64) -> u64 {
    if r > n {
        0
    } else {
        (1..=r.min(n - r)).fold(1, |acc, val| acc * (n - val + 1) / val)
    }
}

The standard library doesn't include any operator or function for calculating the factorial of a number.

Instead you can use the factorial crate or you could use the num crate, which the Rust Cookbook includes an example of. Here's a variation of the Rust Cookbook example, without using the num crate.

fn factorial(n: u64) -> u64 {
    let mut f = 1;
    for i in 1..=n {
        f *= i;
    }
    f
}

As already commented by L. F., this could be expressed shorter using product(), which the documentation for product() includes an example of.

fn factorial(n: u64) -> u64 {
    (1..=n).product()
}

The standard library also doesn't have functions for calculating the number of permutations or combinations a given n and r would give. Instead you can just translate the formulas into functions, like this:

/// n! / (r! * (n - r)!)
fn count_combinations(n: u64, r: u64) -> u64 {
    factorial(n) / (factorial(r) * factorial(n - r))
}

/// n! / (n - r)!
fn count_permutations(n: u64, r: u64) -> u64 {
    factorial(n) / factorial(n - r)
}

fn main() {
    println!("{}", count_combinations(10, 6)); // Prints `210`
    println!("{}", count_permutations(10, 6)); // Prints `151200`
}

If you actually want to produce permutations and/or combinations, then you could use the itertools crate and specifically use the permutations() and combinations() functions.

Related