Rust 练习册 :Pascal‘s Triangle与组合数学
帕斯卡三角形(Pascal’s Triangle)是数学中一个经典的三角形数阵,以法国数学家布莱兹·帕斯卡命名。它在组合数学、概率论、代数等领域有广泛应用。在 Exercism 的 “pascals-triangle” 练习中,我们需要实现一个生成帕斯卡三角形的结构体。这不仅能帮助我们掌握动态规划和数学计算技巧,还能深入学习Rust中的数据结构设计和算法实现。
什么是帕斯卡三角形?
帕斯卡三角形是一个由数字排列成的三角形数阵,其中每个数字是其上方两个数字的和。它的构造规则如下:
- 第0行只有数字1
- 每行的第一个和最后一个数字都是1
- 中间的数字等于上一行相邻两个数字的和
帕斯卡三角形的前几行如下所示:
第0行: 1
第1行: 1 1
第2行: 1 2 1
第3行: 1 3 3 1
第4行: 1 4 6 4 1
第5行: 1 5 10 10 5 1
帕斯卡三角形在以下领域有重要应用:
- 组合数学:第n行第k列的数字表示C(n,k)组合数
- 二项式定理:系数与(a+b)^n的展开式相关
- 概率论:用于计算二项分布的概率
- 计算机科学:动态规划和递归算法的经典例子
让我们先看看练习提供的结构体和函数签名:
pub struct PascalsTriangle;
impl PascalsTriangle {
pub fn new(row_count: u32) -> Self {
unimplemented!("create Pascal's triangle with {} rows", row_count);
}
pub fn rows(&self) -> Vec<Vec<u32>> {
unimplemented!();
}
}
我们需要实现PascalsTriangle结构体,使其能够根据指定的行数生成帕斯卡三角形。
设计分析
1. 核心要求
- 结构体设计:设计合适的结构体存储帕斯卡三角形信息
- 行数处理:正确处理从0行到指定行数的所有行
- 数值计算:准确计算每个位置的数值
- 数据组织:合理组织和返回三角形数据
2. 技术要点
- 动态规划:利用已计算的值计算新值
- 数据结构:选择合适的数据结构存储三角形
- 边界处理:正确处理每行的边界情况
- 类型转换:处理u32到usize等类型的转换
完整实现
1. 基础实现
pub struct PascalsTriangle {
rows: Vec<Vec<u32>>,
}
impl PascalsTriangle {
pub fn new(row_count: u32) -> Self {
let mut triangle = PascalsTriangle {
rows: Vec::new(),
};
for i in 0..row_count {
let mut row = Vec::new();
for j in 0..=i {
if j == 0 || j == i {
// 每行的第一个和最后一个元素都是1
row.push(1);
} else {
// 中间的元素是上一行相邻两个元素的和
let prev_row = &triangle.rows[(i - 1) as usize];
let left = prev_row[(j - 1) as usize];
let right = prev_row[j as usize];
row.push(left + right);
}
}
triangle.rows.push(row);
}
triangle
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
}
2. 优化实现
pub struct PascalsTriangle {
rows: Vec<Vec<u32>>,
}
impl PascalsTriangle {
pub fn new(row_count: u32) -> Self {
let row_count = row_count as usize;
let mut rows: Vec<Vec<u32>> = Vec::with_capacity(row_count);
if row_count == 0 {
return PascalsTriangle { rows };
}
// 初始化第一行
rows.push(vec![1]);
// 生成后续行
for i in 1..row_count {
let mut row = Vec::with_capacity(i + 1);
row.push(1); // 第一个元素总是1
// 计算中间元素
let prev_row = &rows[i - 1];
for j in 1..i {
row.push(prev_row[j - 1] + prev_row[j]);
}
row.push(1); // 最后一个元素总是1
rows.push(row);
}
PascalsTriangle { rows }
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
}
3. 使用组合数公式的实现
pub struct PascalsTriangle {
rows: Vec<Vec<u32>>,
}
impl PascalsTriangle {
pub fn new(row_count: u32) -> Self {
let row_count = row_count as usize;
let mut rows: Vec<Vec<u32>> = Vec::with_capacity(row_count);
for i in 0..row_count {
let mut row = Vec::with_capacity(i + 1);
for j in 0..=i {
row.push(combination(i, j));
}
rows.push(row);
}
PascalsTriangle { rows }
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
}
// 计算组合数 C(n, k)
fn combination(n: usize, k: usize) -> u32 {
if k > n {
return 0;
}
if k == 0 || k == n {
return 1;
}
// 为了防止溢出,使用较小的k值
let k = k.min(n - k);
let mut result = 1u32;
for i in 0..k {
result = result * (n - i) as u32 / (i + 1) as u32;
}
result
}
测试用例分析
通过查看测试用例,我们可以更好地理解需求:
#[test]
fn no_rows() {
let pt = PascalsTriangle::new(0);
let expected: Vec<Vec<u32>> = Vec::new();
assert_eq!(expected, pt.rows());
}
0行应该返回空的向量。
#[test]
fn one_row() {
let pt = PascalsTriangle::new(1);
let expected: Vec<Vec<u32>> = vec![vec![1]];
assert_eq!(expected, pt.rows());
}
1行应该返回[[1]]。
#[test]
fn two_rows() {
let pt = PascalsTriangle::new(2);
let expected: Vec<Vec<u32>> = vec![vec![1], vec![1, 1]];
assert_eq!(expected, pt.rows());
}
2行应该返回[[1], [1, 1]]。
#[test]
fn three_rows() {
let pt = PascalsTriangle::new(3);
let expected: Vec<Vec<u32>> = vec![vec![1], vec![1, 1], vec![1, 2, 1]];
assert_eq!(expected, pt.rows());
}
3行应该返回[[1], [1, 1], [1, 2, 1]]。
#[test]
fn last_of_four_rows() {
let pt = PascalsTriangle::new(4);
let expected: Vec<u32> = vec![1, 3, 3, 1];
assert_eq!(Some(expected), pt.rows().pop());
}
4行的最后一行应该是[1, 3, 3, 1]。
#[test]
fn five_rows() {
let pt = PascalsTriangle::new(5);
let expected: Vec<Vec<u32>> = vec![
vec![1],
vec![1, 1],
vec![1, 2, 1],
vec![1, 3, 3, 1],
vec![1, 4, 6, 4, 1],
];
assert_eq!(expected, pt.rows());
}
5行应该返回完整的5行帕斯卡三角形。
性能优化版本
考虑性能的优化实现:
pub struct PascalsTriangle {
rows: Vec<Vec<u32>>,
}
impl PascalsTriangle {
pub fn new(row_count: u32) -> Self {
let row_count = row_count as usize;
// 预分配向量容量以减少重新分配
let mut rows: Vec<Vec<u32>> = Vec::with_capacity(row_count);
if row_count == 0 {
return PascalsTriangle { rows };
}
// 使用静态初始化避免重新分配
rows.push(vec![1u32]);
// 生成后续行
for i in 1..row_count {
let mut row: Vec<u32> = Vec::with_capacity(i + 1);
// 第一个元素总是1
row.push(1);
// 使用引用避免重复索引
let prev_row = unsafe { rows.get_unchecked(i - 1) };
// 计算中间元素
for j in 1..i {
let sum = unsafe {
*prev_row.get_unchecked(j - 1) + *prev_row.get_unchecked(j)
};
row.push(sum);
}
// 最后一个元素总是1
row.push(1);
rows.push(row);
}
PascalsTriangle { rows }
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
}
// 使用迭代器的版本
pub struct PascalsTriangleIterator {
rows: Vec<Vec<u32>>,
}
impl PascalsTriangleIterator {
pub fn new(row_count: u32) -> Self {
let row_count = row_count as usize;
let mut rows: Vec<Vec<u32>> = Vec::with_capacity(row_count);
if row_count > 0 {
rows.push(vec![1]);
for i in 1..row_count {
let prev_row = &rows[i - 1];
let row = std::iter::once(1)
.chain(prev_row.windows(2).map(|w| w[0] + w[1]))
.chain(std::iter::once(1))
.collect();
rows.push(row);
}
}
PascalsTriangleIterator { rows }
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
}
// 惰性计算版本
pub struct LazyPascalsTriangle {
row_count: usize,
cached_rows: Vec<Vec<u32>>,
}
impl LazyPascalsTriangle {
pub fn new(row_count: u32) -> Self {
LazyPascalsTriangle {
row_count: row_count as usize,
cached_rows: Vec::new(),
}
}
pub fn rows(&mut self) -> &Vec<Vec<u32>> {
// 只在需要时计算行
while self.cached_rows.len() < self.row_count {
let row_index = self.cached_rows.len();
let row = if row_index == 0 {
vec![1]
} else {
let mut row = Vec::with_capacity(row_index + 1);
row.push(1);
let prev_row = &self.cached_rows[row_index - 1];
for j in 1..row_index {
row.push(prev_row[j - 1] + prev_row[j]);
}
row.push(1);
row
};
self.cached_rows.push(row);
}
&self.cached_rows
}
// 获取特定行
pub fn row(&mut self, index: usize) -> Option<&Vec<u32>> {
if index >= self.row_count {
return None;
}
self.rows(); // 确保计算到所需行
self.cached_rows.get(index)
}
}
错误处理和边界情况
考虑更多边界情况的实现:
pub struct PascalsTriangle {
rows: Vec<Vec<u32>>,
}
#[derive(Debug, PartialEq)]
pub enum TriangleError {
Overflow,
InvalidRowCount,
}
impl std::fmt::Display for TriangleError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
TriangleError::Overflow => write!(f, "计算过程中发生溢出"),
TriangleError::InvalidRowCount => write!(f, "行数无效"),
}
}
}
impl std::error::Error for TriangleError {}
impl PascalsTriangle {
pub fn new(row_count: u32) -> Self {
// 处理边界情况
if row_count == 0 {
return PascalsTriangle {
rows: Vec::new(),
};
}
let row_count = row_count as usize;
let mut rows: Vec<Vec<u32>> = Vec::with_capacity(row_count);
// 初始化第一行
rows.push(vec![1]);
// 生成后续行
for i in 1..row_count {
let mut row = Vec::with_capacity(i + 1);
row.push(1); // 第一个元素总是1
// 计算中间元素
let prev_row = &rows[i - 1];
for j in 1..i {
// 检查溢出
match prev_row[j - 1].checked_add(prev_row[j]) {
Some(sum) => row.push(sum),
None => panic!("在计算帕斯卡三角形时发生溢出"),
}
}
row.push(1); // 最后一个元素总是1
rows.push(row);
}
PascalsTriangle { rows }
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
// 安全版本,返回Result
pub fn try_new(row_count: u32) -> Result<Self, TriangleError> {
// 处理边界情况
if row_count == 0 {
return Ok(PascalsTriangle {
rows: Vec::new(),
});
}
let row_count = row_count as usize;
let mut rows: Vec<Vec<u32>> = Vec::with_capacity(row_count);
// 初始化第一行
rows.push(vec![1]);
// 生成后续行
for i in 1..row_count {
let mut row = Vec::with_capacity(i + 1);
row.push(1); // 第一个元素总是1
// 计算中间元素
let prev_row = &rows[i - 1];
for j in 1..i {
// 检查溢出
match prev_row[j - 1].checked_add(prev_row[j]) {
Some(sum) => row.push(sum),
None => return Err(TriangleError::Overflow),
}
}
row.push(1); // 最后一个元素总是1
rows.push(row);
}
Ok(PascalsTriangle { rows })
}
// 获取行数
pub fn row_count(&self) -> usize {
self.rows.len()
}
// 获取特定行
pub fn get_row(&self, index: usize) -> Option<&Vec<u32>> {
self.rows.get(index)
}
// 获取特定位置的值
pub fn get_value(&self, row: usize, col: usize) -> Option<u32> {
self.rows.get(row).and_then(|r| r.get(col).copied())
}
}
// 使用u64以支持更大的数值
pub struct PascalsTriangle64 {
rows: Vec<Vec<u64>>,
}
impl PascalsTriangle64 {
pub fn new(row_count: u32) -> Self {
let row_count = row_count as usize;
let mut rows: Vec<Vec<u64>> = Vec::with_capacity(row_count);
if row_count == 0 {
return PascalsTriangle64 { rows };
}
rows.push(vec![1]);
for i in 1..row_count {
let mut row = Vec::with_capacity(i + 1);
row.push(1);
let prev_row = &rows[i - 1];
for j in 1..i {
row.push(prev_row[j - 1] + prev_row[j]);
}
row.push(1);
rows.push(row);
}
PascalsTriangle64 { rows }
}
pub fn rows(&self) -> Vec<Vec<u64>> {
self.rows.clone()
}
}
扩展功能
基于基础实现,我们可以添加更多功能:
pub struct PascalsTriangle {
rows: Vec<Vec<u32>>,
}
impl PascalsTriangle {
pub fn new(row_count: u32) -> Self {
let row_count = row_count as usize;
let mut rows: Vec<Vec<u32>> = Vec::with_capacity(row_count);
if row_count == 0 {
return PascalsTriangle { rows };
}
rows.push(vec![1]);
for i in 1..row_count {
let mut row = Vec::with_capacity(i + 1);
row.push(1);
let prev_row = &rows[i - 1];
for j in 1..i {
row.push(prev_row[j - 1] + prev_row[j]);
}
row.push(1);
rows.push(row);
}
PascalsTriangle { rows }
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
// 获取行数
pub fn row_count(&self) -> usize {
self.rows.len()
}
// 获取特定行
pub fn get_row(&self, index: usize) -> Option<&Vec<u32>> {
self.rows.get(index)
}
// 获取特定位置的值
pub fn get_value(&self, row: usize, col: usize) -> Option<u32> {
self.rows.get(row).and_then(|r| r.get(col).copied())
}
// 计算某行的和
pub fn row_sum(&self, row_index: usize) -> Option<u32> {
self.rows.get(row_index).map(|row| row.iter().sum())
}
// 获取对称轴右侧的值(不包括中间值)
pub fn right_half(&self, row_index: usize) -> Option<Vec<u32>> {
self.rows.get(row_index).map(|row| {
let mid = (row.len() + 1) / 2;
row.iter().skip(mid).copied().collect()
})
}
// 判断是否包含某个值
pub fn contains(&self, value: u32) -> bool {
self.rows.iter().any(|row| row.contains(&value))
}
// 查找值的位置
pub fn find(&self, value: u32) -> Vec<(usize, usize)> {
let mut positions = Vec::new();
for (row_index, row) in self.rows.iter().enumerate() {
for (col_index, &val) in row.iter().enumerate() {
if val == value {
positions.push((row_index, col_index));
}
}
}
positions
}
// 格式化显示
pub fn display(&self) -> String {
let mut result = String::new();
for (i, row) in self.rows.iter().enumerate() {
// 添加适当的前导空格以居中显示
let padding = " ".repeat((self.rows.len() - i - 1) * 2);
result.push_str(&padding);
for (j, &value) in row.iter().enumerate() {
if j > 0 {
result.push(' ');
}
result.push_str(&value.to_string());
}
result.push('\n');
}
result
}
}
// 帕斯卡三角形的统计信息
pub struct TriangleStats {
pub row_count: usize,
pub total_elements: usize,
pub max_value: u32,
pub max_value_position: (usize, usize),
pub even_count: usize,
pub odd_count: usize,
}
impl PascalsTriangle {
pub fn stats(&self) -> TriangleStats {
let row_count = self.rows.len();
let total_elements: usize = self.rows.iter().map(|row| row.len()).sum();
let mut max_value = 0;
let mut max_value_position = (0, 0);
let mut even_count = 0;
let mut odd_count = 0;
for (row_index, row) in self.rows.iter().enumerate() {
for (col_index, &value) in row.iter().enumerate() {
if value > max_value {
max_value = value;
max_value_position = (row_index, col_index);
}
if value % 2 == 0 {
even_count += 1;
} else {
odd_count += 1;
}
}
}
TriangleStats {
row_count,
total_elements,
max_value,
max_value_position,
even_count,
odd_count,
}
}
}
// 生成器模式实现
pub struct TriangleGenerator {
current_row: Vec<u32>,
next_row_index: usize,
max_rows: usize,
}
impl TriangleGenerator {
pub fn new(max_rows: usize) -> Self {
TriangleGenerator {
current_row: vec![1],
next_row_index: 1,
max_rows,
}
}
}
impl Iterator for TriangleGenerator {
type Item = Vec<u32>;
fn next(&mut self) -> Option<Self::Item> {
if self.next_row_index > self.max_rows {
return None;
}
let result = self.current_row.clone();
if self.next_row_index < self.max_rows {
let mut next_row = Vec::with_capacity(self.next_row_index + 1);
next_row.push(1);
for i in 1..self.next_row_index {
next_row.push(self.current_row[i - 1] + self.current_row[i]);
}
next_row.push(1);
self.current_row = next_row;
}
self.next_row_index += 1;
Some(result)
}
}
// 便利函数
pub fn generate_pascals_triangle(row_count: u32) -> Vec<Vec<u32>> {
PascalsTriangle::new(row_count).rows()
}
pub fn print_pascals_triangle(row_count: u32) {
let triangle = PascalsTriangle::new(row_count);
println!("{}", triangle.display());
}
实际应用场景
帕斯卡三角形在实际开发中有以下应用:
- 组合数学:计算组合数和排列数
- 概率论:二项分布的概率计算
- 算法教学:递归和动态规划的经典例子
- 计算机图形学:贝塞尔曲线的计算
- 金融数学:期权定价模型
- 密码学:某些加密算法中的数学计算
- 数据分析:统计分布的计算
- 教育工具:数学教学演示
算法复杂度分析
-
时间复杂度:O(n²)
- 其中n是行数,需要计算n(n+1)/2个元素
-
空间复杂度:O(n²)
- 需要存储n(n+1)/2个元素
与其他实现方式的比较
// 使用递归的实现
pub struct RecursivePascalsTriangle {
rows: Vec<Vec<u32>>,
}
impl RecursivePascalsTriangle {
pub fn new(row_count: u32) -> Self {
let mut triangle = RecursivePascalsTriangle {
rows: Vec::new(),
};
for i in 0..row_count {
let mut row = Vec::new();
for j in 0..=i {
row.push(Self::pascal_value(i, j));
}
triangle.rows.push(row);
}
triangle
}
// 递归计算帕斯卡三角形的值
fn pascal_value(row: u32, col: u32) -> u32 {
if col == 0 || col == row {
1
} else {
Self::pascal_value(row - 1, col - 1) + Self::pascal_value(row - 1, col)
}
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
}
// 使用记忆化的递归实现
use std::collections::HashMap;
pub struct MemoizedPascalsTriangle {
rows: Vec<Vec<u32>>,
memo: HashMap<(u32, u32), u32>,
}
impl MemoizedPascalsTriangle {
pub fn new(row_count: u32) -> Self {
let mut triangle = MemoizedPascalsTriangle {
rows: Vec::new(),
memo: HashMap::new(),
};
for i in 0..row_count {
let mut row = Vec::new();
for j in 0..=i {
row.push(Self::pascal_value_memo(i, j, &mut triangle.memo));
}
triangle.rows.push(row);
}
triangle
}
// 带记忆化的递归计算
fn pascal_value_memo(row: u32, col: u32, memo: &mut HashMap<(u32, u32), u32>) -> u32 {
if col == 0 || col == row {
return 1;
}
if let Some(&value) = memo.get(&(row, col)) {
return value;
}
let value = Self::pascal_value_memo(row - 1, col - 1, memo) +
Self::pascal_value_memo(row - 1, col, memo);
memo.insert((row, col), value);
value
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
}
// 使用数学公式的实现
pub struct FormulaPascalsTriangle {
rows: Vec<Vec<u32>>,
}
impl FormulaPascalsTriangle {
pub fn new(row_count: u32) -> Self {
let mut rows: Vec<Vec<u32>> = Vec::with_capacity(row_count as usize);
for i in 0..row_count {
let mut row = Vec::with_capacity((i + 1) as usize);
for j in 0..=i {
row.push(Self::combination(i, j));
}
rows.push(row);
}
FormulaPascalsTriangle { rows }
}
// 使用组合数公式计算
fn combination(n: u32, k: u32) -> u32 {
if k > n {
return 0;
}
if k == 0 || k == n {
return 1;
}
let k = k.min(n - k); // 利用对称性
let mut result = 1u32;
for i in 0..k {
result = result * (n - i) / (i + 1);
}
result
}
pub fn rows(&self) -> Vec<Vec<u32>> {
self.rows.clone()
}
}
// 使用第三方库的实现
// [dependencies]
// num = "0.4"
use num::BigUint;
pub struct BigPascalsTriangle {
rows: Vec<Vec<BigUint>>,
}
impl BigPascalsTriangle {
pub fn new(row_count: u32) -> Self {
let row_count = row_count as usize;
let mut rows: Vec<Vec<BigUint>> = Vec::with_capacity(row_count);
if row_count == 0 {
return BigPascalsTriangle { rows };
}
rows.push(vec![BigUint::from(1u32)]);
for i in 1..row_count {
let mut row = Vec::with_capacity(i + 1);
row.push(BigUint::from(1u32));
let prev_row = &rows[i - 1];
for j in 1..i {
row.push(&prev_row[j - 1] + &prev_row[j]);
}
row.push(BigUint::from(1u32));
rows.push(row);
}
BigPascalsTriangle { rows }
}
pub fn rows(&self) -> Vec<Vec<BigUint>> {
self.rows.clone()
}
}
总结
通过 pascals-triangle 练习,我们学到了:
- 动态规划:掌握了利用已计算结果计算新结果的方法
- 数据结构设计:学会了设计合适的数据结构存储复杂数据
- 边界处理:深入理解了各种边界情况的处理
- 数学算法实现:了解了如何将数学概念转换为代码实现
- 性能优化:学会了预分配内存和避免重复计算等优化技巧
- 错误处理:理解了如何处理计算过程中的潜在错误
这些技能在实际开发中非常有用,特别是在算法设计、数学计算、数据处理等场景中。帕斯卡三角形虽然是一个经典的数学问题,但它涉及到了动态规划、数据结构设计、边界处理等许多核心概念,是学习Rust实用编程的良好起点。
通过这个练习,我们也看到了Rust在数学计算和算法实现方面的强大能力,以及如何用安全且高效的方式实现经典算法。这种结合了安全性和性能的语言特性正是Rust的魅力所在。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)