Rust 첫걸음 연습 문서의 문제 4 풀이 코드다.
//! 04 Rust 첫걸음 연습 p4: a < b < c, a + b + c = sum인 첫 피타고라스 세 수를 레이블 블록과 레이블 break로 찾는다.
//! 의존성: 없음 (표준 라이브러리만)
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/4.연습문제/04_rust-basics-practice_p4.rs"
/// a를 작은 것부터, 그 안에서 b를 작은 것부터 돌며 처음 찾은 (a, b, c)를 돌려준다.
fn triple(sum: u32) -> Option<(u32, u32, u32)> {
'found: {
for a in 1..sum {
for b in (a + 1)..sum {
let Some(c) = sum.checked_sub(a + b) else {
break; // a + b가 sum을 넘었다: 이 a에서 더 큰 b는 볼 필요가 없다
};
if c <= b {
break; // b가 커질수록 c는 작아진다: 이 a는 끝
}
if a * a + b * b == c * c {
break 'found Some((a, b, c));
}
}
}
None
}
}
/// 같은 일을 하는 브루트포스: 모든 (a, b, c)를 본다.
fn triple_brute(sum: u32) -> Option<(u32, u32, u32)> {
for a in 1..sum {
for b in (a + 1)..sum {
for c in (b + 1)..=sum {
if a + b + c == sum && a * a + b * b == c * c {
return Some((a, b, c));
}
}
}
}
None
}
/// 변형: 안쪽 break를 레이블 break로 바꾼 틀린 판.
fn triple_wrong(sum: u32) -> Option<(u32, u32, u32)> {
'found: {
for a in 1..sum {
for b in (a + 1)..sum {
let Some(c) = sum.checked_sub(a + b) else {
break 'found None;
};
if c <= b {
break 'found None;
}
if a * a + b * b == c * c {
break 'found Some((a, b, c));
}
}
}
None
}
}
fn main() {
assert_eq!(triple(12), Some((3, 4, 5)));
assert_eq!(triple(30), Some((5, 12, 13)));
assert_eq!(triple(1000), Some((200, 375, 425)));
assert_eq!(triple(10), None);
let (a, b, c) = triple(1000).unwrap();
assert_eq!(a * b * c, 31_875_000);
println!(
"12 -> {:?}, 30 -> {:?}, 1000 -> {:?}, 곱 {}",
triple(12),
triple(30),
triple(1000),
a * b * c
);
for s in 1..=120 {
assert_eq!(triple(s), triple_brute(s), "sum = {s}");
}
println!("sum 1..=120에서 브루트포스와 일치");
assert_eq!(triple_wrong(1000), None);
assert_eq!(triple_wrong(12), None);
println!(
"변형: 레이블 break로 바꾸면 1000 -> {:?}",
triple_wrong(1000)
);
println!("ALL CHECKS PASSED");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn example() {
assert_eq!(triple(12), Some((3, 4, 5)));
assert_eq!(triple(10), None);
}
}