「もっとプログラマ脳を鍛える数学パズル」を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));
}
}
確かに同一の結果になる。コードも分かりやすい。