Rust前缀和优化库prefix-sum-vec的使用,高效实现区间查询和动态更新的数据结构

Rust前缀和优化库prefix-sum-vec的使用,高效实现区间查询和动态更新的数据结构

这是什么?

这个crate提供的数据结构允许用户以空间高效的方式存储重复的相同值序列。这种数据结构的一个应用场景是在WebAssembly中,函数的本地变量被表示为类型及其重复计数序列。例如:

(locals i32 i32 i32 i32 i64 i64 f64)

实际上会被编码为类似0x04 i32 0x02 i64 0x01 f64的序列。如果本地变量的解码表示将所有内容简单地放入向量中,可能会出现拒绝服务攻击的危险。

这个crate提供的数据结构只存储重复元素的一个副本,同时存储元素所在索引的前缀和。对于上面的本地变量示例,存储看起来像这样:

[(4, i32), (6, i64), (7, f64)]

查找索引为5的元素只需对前缀和索引进行二分搜索即可。

使用方式

首先,在您的Cargo.toml中指定这个crate:

[dependencies]
prefix-sum-vec = "0.1"

这个crate旨在非常轻量级和简单直接。因此目前没有可选功能需要启用。然后,参考PrefixSumVec类型的文档来了解如何在您的代码中使用它。

完整示例

以下是使用prefix-sum-vec库的完整示例:

use prefix_sum_vec::PrefixSumVec;

fn main() {
    // 创建一个前缀和向量
    let mut psv = PrefixSumVec::new();
    
    // 添加重复元素
    psv.push(4, "i32");    // 4个i32
    psv.push(2, "i64");    // 2个i64
    psv.push(1, "f64");    // 1个f64
    
    // 查询元素
    println!("Index 3: {:?}", psv.get(3)); // 应该返回Some("i32")
    println!("Index 5: {:?}", psv.get(5)); // 应该返回Some("i64")
    println!("Index 6: {:?}", psv.get(6)); // 应该返回Some("f64")
    println!("Index 7: {:?}", psv.get(7)); // 应该返回None
    
    // 迭代所有元素
    for (i, val) in psv.iter() {
        println!("Index {}: {}", i, val);
    }
}

扩展示例

这里是一个更完整的示例,展示更多功能:

use prefix_sum_vec::PrefixSumVec;

fn main() {
    // 创建并初始化一个前缀和向量
    let mut psv = PrefixSumVec::from(vec![
        (3, "Rust"),    // 3个"Rust"
        (2, "Go"),      // 2个"Go"
        (4, "Python")   // 4个"Python"
    ]);
    
    // 查询元素
    println!("Index 2: {:?}", psv.get(2)); // Some("Rust")
    println!("Index 4: {:?}", psv.get(4)); // Some("Go")
    println!("Index 5: {:?}", psv.get(5)); // Some("Python")
    
    // 更新元素
    psv.update(4, "Java"); // 将索引4处的值改为"Java"
    println!("After update - Index 4: {:?}", psv.get(4)); // Some("Java")
    
    // 获取总长度
    println!("Total length: {}", psv.len());
    
    // 范围查询
    for (i, val) in psv.range(2..6) {
        println!("Range index {}: {}", i, val);
    }
    
    // 转换为普通向量
    let vec: Vec<_> = psv.into_iter().collect();
    println!("Converted vector: {:?}", vec);
}

许可证

这个项目采用Apache 2.0或BSD 3-clause许可证,您可以选择其中一种。


1 回复

Rust前缀和优化库prefix-sum-vec的使用

介绍

prefix-sum-vec 是一个高效的Rust库,实现了前缀和数据结构,支持快速的区间查询和动态更新。它特别适用于需要频繁计算数组区间和并可能同时更新元素的场景。

主要特性

  • 高效区间求和:O(log n)时间复杂度
  • 单点更新:O(log n)时间复杂度
  • 空间效率:O(n)空间复杂度
  • 支持泛型,可用于各种数值类型

使用方法

首先在Cargo.toml中添加依赖:

[dependencies]
prefix-sum-vec = "0.1"

基本使用示例

use prefix_sum_vec::PrefixSumVec;

fn main() {
    // 从数组创建前缀和向量
    let mut psv = PrefixSumVec::from(vec![1, 2, 3, 4, 5]);
    
    // 查询区间和 [0..2] (左闭右开)
    let sum = psv.range_sum(0, 2);
    println!("Sum of range [0..2]: {}", sum); // 输出: 3 (1 + 2)
    
    // 更新元素
    psv.set(1, 5); // 将索引1处的值从2改为5
    
    // 再次查询
    let new_sum = psv.range_sum(0, 2);
    println!("New sum of range [0..2]: {}", new_sum); // 输出: 6 (1 + 5)
}

高级用法

use prefix_sum_vec::PrefixSumVec;

fn main() {
    // 创建空的前缀和向量并逐步添加元素
    let mut psv = PrefixSumVec::new();
    psv.push(10);
    psv.push(20);
    psv.push(30);
    
    // 查询整个范围的和
    println!("Total sum: {}", psv.range_sum(0, psv.len())); // 输出: 60
    
    // 使用迭代器创建
    let psv2: PrefixSumVec<i32> = (1..=10).collect();
    println!("Sum of 1..10: {}", psv2.range_sum(0, 10)); // 输出: 55
    
    // 使用自定义加法运算
    #[derive(Clone, Copy)]
    struct Point { x: i32, y: i32 }
    
    impl std::ops::Add for Point {
        type Output = Self;
        fn add(self, rhs: Self) -> Self {
            Point { x: self.x + rhs.x, y: self.y + rhs.y }
        }
    }
    
    let points = vec![Point { x:1, y:2 }, Point { x:3, y:4 }];
    let psv_points = PrefixSumVec::from(points);
    let total = psv_points.range_sum(0, 2);
    println!("Total point: ({}, {})", total.x, total.y); // 输出: (4, 6)
}

完整示例demo

下面是一个结合基本使用和高级用法的完整示例:

use prefix_sum_vec::PrefixSumVec;

fn main() {
    // 示例1: 基本使用
    println!("=== 基本使用示例 ===");
    let mut basic_psv = PrefixSumVec::from(vec![10, 20, 30, 40, 50]);
    
    // 查询区间和
    println!("区间[1..4]的和: {}", basic_psv.range_sum(1, 4)); // 20+30+40=90
    
    // 更新元素并重新查询
    basic_psv.set(2, 100); // 将索引2的值从30改为100
    println!("更新后的区间[1..4]的和: {}", basic_psv.range_sum(1, 4)); // 20+100+40=160
    
    // 示例2: 高级用法 - 自定义类型
    println!("\n=== 高级用法示例 - 自定义类型 ===");
    #[derive(Clone, Copy, Debug)]
    struct InventoryItem {
        quantity: i32,
        price: f32,
    }
    
    impl std::ops::Add for InventoryItem {
        type Output = Self;
        fn add(self, rhs: Self) -> Self {
            InventoryItem {
                quantity: self.quantity + rhs.quantity,
                price: self.price + rhs.price,
            }
        }
    }
    
    let inventory = vec![
        InventoryItem { quantity: 5, price: 9.99 },
        InventoryItem { quantity: 10, price: 14.99 },
        InventoryItem { quantity: 3, price: 4.99 },
    ];
    
    let psv_inventory = PrefixSumVec::from(inventory);
    let total = psv_inventory.range_sum(0, psv_inventory.len());
    println!("总库存: {:?}", total); // 数量18, 价格29.97
    
    // 示例3: 性能优化 - 批量操作
    println!("\n=== 性能优化示例 - 批量操作 ===");
    let large_data: Vec<i64> = (1..=100_000).map(|x| x * x).collect();
    
    // 使用with_capacity预分配空间
    let mut psv_large = PrefixSumVec::with_capacity(100_000);
    for &num in &large_data[..50_000] {
        psv_large.push(num);
    }
    
    // 批量添加剩余数据
    psv_large.extend_from_slice(&large_data[50_000..]);
    
    // 查询大范围的和
    println!("前50000项的和: {}", psv_large.range_sum(0, 50_000));
    println!("全部100000项的和: {}", psv_large.range_sum(0, 100_000));
}

性能优化提示

  1. 批量更新:如果需要更新多个元素,考虑使用fromwith_capacity重建数据结构,而不是多次调用set

  2. 预分配空间:如果知道元素数量,使用with_capacity避免重新分配

  3. 对于只读场景,考虑使用标准的前缀和数组以获得更快的查询速度(O(1))

适用场景

  • 频繁区间求和计算
  • 数组元素会动态更新
  • 统计分析和数值计算
  • 游戏开发中的资源管理
  • 算法竞赛中的高效实现

这个库在需要平衡查询和更新操作的场景下特别有用,比朴素的前缀和数组(更新慢)和普通数组(查询慢)提供了更好的综合性能。

回到顶部