组合/排列数

问题描述

如何在 Rust 中找到排列或组合的数量

例如C(10,6) = 210

我在标准库中找不到这个函数,也找不到阶乘运算符(这就足够了)。

解决方法

根据@vallentin 的回答,可以进行许多优化。让我们使用相同的阶乘函数(暂时):

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

对于 count_permutationsn! / (n - r)! 实际上只是 n - r + 1n(含)之间所有数字的乘积,因此我们甚至不需要计算 2 个阶乘(这可能会溢出并涉及计算重叠数字的乘积):

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

我们可以对 count_combinations 进行类似的优化:

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

count_permutations 几乎完全优化,既快速又正确(如果 count_permutations 的结果可以放入 u64 中,那么它应该永远不会溢出)。

count_combinations 仍然有一些缺陷,即由于它是计算乘积然后除法,其结果可能适合u64,但该函数仍然会溢出。您可以通过交替的乘法和除法使其非常接近非溢出:

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_combinations(n: u64,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));
}

请注意,您可以进行一些微优化,即对 r.min(n - r) 使用 r 而不是 count_combinations,因为 count_combinations(n,r) == count_combinations(n,n - r),并使用较小的两者中的一个将缩小循环大小:

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

标准库不包含任何用于计算数字阶乘的运算符或函数。

相反,您可以使用 factorial crate 或您可以使用 num crate,其中 Rust Cookbook 包括一个示例。这是 Rust Cookbook 示例的变体,不使用 num 板条箱。

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

commented by L. F. 一样,这可以使用 product() 表达得更短,product() 的文档中包含一个示例。

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

标准库也没有用于计算给定 nr 会给出的排列或组合数量的函数。相反,您可以将公式转换为函数,如下所示:

/// 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!("{}",6)); // Prints `210`
    println!("{}",6)); // Prints `151200`
}

如果您确实想要产生排列和/或组合,那么您可以使用 itertools crate 并专门使用 permutations()combinations() 函数。