LoginSignup
1
1

More than 3 years have passed since last update.

「もっとプログラム脳を鍛える数学パズル」_pp.018-020 (code:Ruby) -> Rust

Last updated at Posted at 2020-09-29

「もっとプログラマ脳を鍛える数学パズル」をRustで書き直すのは、ボケ防止にちょうど良いかもしれない、と思った。

例題2 順列と組み合わせ(pp.018-020)

Ruby

なんだか書籍p.018のpre2_1.rbは、正しい答えが出てない気がする。
定義からすると、順列のコードは、

r.upto(n) do |i|

ではない、よな・・・。
ということで、正しい答えが出るコード。

ex2.rb
require "test/unit"

def nPr(n, r)
    result = 1
    (n-r+1).upto(n) do |i|
        result *= i
    end
    result
end

def nCr(n,r)
    result = 1
    1.upto(r) do |i|
        result = result * (n - i + 1) / i
    end
    result
end

class NPrTest < Test::Unit::TestCase
    def test_nPr
        assert_equal 1, nPr(1,1), "1P1 should return 1."
        assert_equal 720, nPr(6,6), "6P6 should return 720."
        assert_equal 20, nPr(5,2), "5P2 should return 20."
    end

    def test_nCr
        assert_equal 1, nCr(1,1), "1C1 should return 1."
        assert_equal 1, nCr(6,6), "6C6 should return 1."
        assert_equal 10, nCr(5,2), "5C2 should return 10."
    end
end

Rust

main.rs
fn main() {
    println!("{n}P{r} = {x}", n = 5, r = 2, x = permutation(5, 2));
    println!("{n}C{r} = {x}", n = 5, r = 2, x = combination(5, 2));
}

pub fn permutation(n: i64, r: i64) -> i64 {
    let mut result = 1;
    for i in (n - r + 1)..=n {
        result *= i;
    }
    return result;
}

pub fn combination(n: i64, r: i64) -> i64 {
    let mut result = 1;
    for i in 1..=r {
        result = result * (n - i + 1) / i;
    }
    return result;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_permutation() {
        assert_eq!(permutation(1, 1), 1);
        assert_eq!(permutation(6, 6), 720);
        assert_eq!(permutation(5, 2), 20);
    }

    #[test]
    fn test_combination() {
        assert_eq!(combination(1, 1), 1);
        assert_eq!(combination(6, 6), 1);
        assert_eq!(combination(5, 2), 10);
    }
}

ベタ移植。ところで、

result = result * (n - i + 1) / i;

と書くのと、

result *= (n - i + 1) / i;

と書くのは違うのか。テストを書きながら進めてよかった。

mutを使いまくるのはどうなのか」と考えてしまうが、1関数内に閉じている限りは、騒ぐほどのこともないと思う。

2020/09/29 追記

簡潔なコード例を頂いた。permutation2()combination2()として実験。

main.rs
fn main() {
    println!("{n}P{r} = {x}", n = 5, r = 2, x = permutation(5, 2));
    println!("{n}C{r} = {x}", n = 5, r = 2, x = combination(5, 2));
}

pub fn permutation(n: i64, r: i64) -> i64 {
    let mut result = 1;
    for i in (n - r + 1)..=n {
        result *= i;
    }
    return result;
}

pub fn combination(n: i64, r: i64) -> i64 {
    let mut result = 1;
    for i in 1..=r {
        result = result * (n - i + 1) / i;
    }
    return result;
}

pub fn permutation2(n: i64, r: i64) -> i64 {
    (n - r + 1..=n).product()
}

pub fn combination2(n: i64, r: i64) -> i64 {
    (1..=r).fold(1, |p, i| p * (n - i + 1) / i)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_permutation() {
        assert_eq!(permutation(1, 1), 1);
        assert_eq!(permutation(6, 6), 720);
        assert_eq!(permutation(5, 2), 20);
        assert_eq!(permutation(1,1), permutation2(1,1));
        assert_eq!(permutation(6,6), permutation2(6,6));
        assert_eq!(permutation(5,2), permutation2(5,2));
    }

    #[test]
    fn test_combination() {
        assert_eq!(combination(1, 1), 1);
        assert_eq!(combination(6, 6), 1);
        assert_eq!(combination(5, 2), 10);
        assert_eq!(combination(1, 1), combination2(1,1));
        assert_eq!(combination(6, 6), combination2(6,6));
        assert_eq!(combination(5, 2), combination2(5,2));
    }
}

確かに同一の結果になる。コードも分かりやすい。

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1