#memoization #macro #proc-macro #optimization #dp

过程宏 dp_macro

实现动态规划 memoization 的过程宏

2 个版本

0.3.2 2024 年 8 月 2 日
0.3.1 2024 年 8 月 2 日

1180开发工具

Download history 144/week @ 2024-07-29 32/week @ 2024-08-05

176 每月下载

GPL-3.0-or-later

16KB
188

dp_rust

Rust 过程宏,用于将 memoization 应用到纯函数。

此 crate 仍处于 beta 版。如果可能,请报告问题。

用法

有关 memoization 的解释,请参阅说明

实现 memoization 简单,但需要一些时间和添加样板代码。

dp 属性

只需将原始 fn 和在开头添加 #[dp] 即可。

fn main(){
  assert_eq!(102334155, fib(40));
}

#[dp]
fn fib(n: i32) -> i32{
  if n < 2{
    return n;
  }
  (fib(n-1) + fib(n-2)) % 1_000_000_007
}

函数不能在 impl 内部。

在非纯函数上使用 dp 宏是未定义行为。注意,纯不意味着 const,例如,尽管在 const 中禁止循环,但您仍然可以使用循环。

所有参数以及输出都必须实现 Clone

额外参数

如果需要,可以使用 #[dp_extra] 属性包含额外的不可变参数。

fn main(){
  assert_eq!(
    knapsack(vec![3, 4, 5, 6, 10], vec![2, 3, 4, 5, 9], 5, 10),
    13
  );
}

#[dp]
#[dp_extra(values: Vec<i32>, weights: Vec<i32>)]
fn knapsack(n: usize, k: i32){
  if n == 0 {
    return 0;
  }
  let mut ans = knapsack(n - 1, k);
  if k >= weights[n - 1] {
    ans = std::cmp::max(ans, knapsack(n - 1, k - weights[n - 1]) + values[n - 1]);
  }
  return ans;

}

顺序是首先所有额外参数,然后是按出现顺序的所有函数参数。

默认参数

#[dp_default] 属性允许您删除最终函数中应默认的辅助参数。

use std::cmp::min;

fn main(){
  assert_eq!(edit_distance("movie", "love"), 2);
}


#[dp]
#[dp_extra(a: String, b: String)]
#[dp_default(i=a.len(); j=b.len())]
fn edit_distance(i: usize, j: usize) -> usize{
  if i == 0{
    return j;
  }
  if j == 0{
    return i;
  }
  let mut ans = min(edit_distance(i-1, j), edit_distance(i, j-1))+1;
  if a.as_bytes()[i-1] == b.as_bytes()[j-1]{
    ans = min(ans, edit_distance(i-1, j-1));
  }
  else{
    ans = min(ans, edit_distance(i-1, j-1)+1);
  }
  return ans;
}

如上所示,可以使用默认参数 n = values.len() 实现背包问题。

说明

Memoization 是一种技术,允许通过保存答案而不会重新计算来优化纯函数的重叠子问题。

例如,考虑斐波那契函数,给定递归

$f(n) = f(n-1) + f(n-2)$,其中$f(0) = 0$和$f(1) = 1$

由于函数只能返回最多1个答案来结束递归调用,可以证明递归调用的次数至少是第n个斐波那契数,其增长呈指数级。但给定前n个斐波那契数,计算第n+1个数只需要两次内存查找和一个求和操作,现在问题已经线性化了。

注意,如果常数函数循环不起作用,例如:$f(n) = f(n+1) + f(n-1)$

有些问题,尽管使用了常数非循环函数,但重叠子问题的数量接近于零,比如回溯法。在这种情况下,备忘录技术无助于提高效率,反而会增加开销,使问题变得更糟。

依赖关系

~270–720KB
~17K SLoC