// 1051: Fibonacci with HashMap Memoization
use std::collections::HashMap;
// Approach 1: Iterative with HashMap cache
fn fib_memo(n: u64, cache: &mut HashMap<u64, u64>) -> u64 {
if n <= 1 {
return n;
}
if let Some(&v) = cache.get(&n) {
return v;
}
let v = fib_memo(n - 1, cache) + fib_memo(n - 2, cache);
cache.insert(n, v);
v
}
// Approach 2: Closure-based memoizer
fn make_fib_memo() -> impl FnMut(u64) -> u64 {
let mut cache = HashMap::new();
move |n| {
fn inner(n: u64, cache: &mut HashMap<u64, u64>) -> u64 {
if n <= 1 {
return n;
}
if let Some(&v) = cache.get(&n) {
return v;
}
let v = inner(n - 1, cache) + inner(n - 2, cache);
cache.insert(n, v);
v
}
inner(n, &mut cache)
}
}
// Approach 3: Generic memoization wrapper
struct Memoize<F> {
cache: HashMap<u64, u64>,
func: F,
}
impl<F: Fn(u64, &mut dyn FnMut(u64) -> u64) -> u64> Memoize<F> {
fn new(func: F) -> Self {
Memoize {
cache: HashMap::new(),
func,
}
}
fn call(&mut self, n: u64) -> u64 {
if let Some(&v) = self.cache.get(&n) {
return v;
}
// We need to use unsafe here or restructure; instead use a simpler pattern
let mut cache = std::mem::take(&mut self.cache);
let v = (self.func)(n, &mut |x| {
if x <= 1 {
return x;
}
if let Some(&v) = cache.get(&x) {
return v;
}
// For deeply nested, fall back to iterative
let mut a = 0u64;
let mut b = 1u64;
for _ in 2..=x {
let t = a + b;
a = b;
b = t;
}
cache.insert(x, b);
b
});
cache.insert(n, v);
self.cache = cache;
v
}
}
fn main() {
let mut cache = HashMap::new();
println!("fib(10) = {}", fib_memo(10, &mut cache));
println!("fib(20) = {}", fib_memo(20, &mut cache));
println!("fib(30) = {}", fib_memo(30, &mut cache));
let mut fib = make_fib_memo();
println!("closure fib(30) = {}", fib(30));
let mut memo = Memoize::new(|n, recurse: &mut dyn FnMut(u64) -> u64| {
if n <= 1 {
n
} else {
recurse(n - 1) + recurse(n - 2)
}
});
println!("generic fib(30) = {}", memo.call(30));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fib_memo() {
let mut cache = HashMap::new();
assert_eq!(fib_memo(0, &mut cache), 0);
assert_eq!(fib_memo(1, &mut cache), 1);
assert_eq!(fib_memo(10, &mut cache), 55);
assert_eq!(fib_memo(20, &mut cache), 6765);
assert_eq!(fib_memo(30, &mut cache), 832040);
}
#[test]
fn test_fib_closure() {
let mut fib = make_fib_memo();
assert_eq!(fib(0), 0);
assert_eq!(fib(1), 1);
assert_eq!(fib(10), 55);
assert_eq!(fib(20), 6765);
assert_eq!(fib(30), 832040);
}
#[test]
fn test_fib_generic() {
let mut memo = Memoize::new(|n, recurse: &mut dyn FnMut(u64) -> u64| {
if n <= 1 { n } else { recurse(n - 1) + recurse(n - 2) }
});
assert_eq!(memo.call(0), 0);
assert_eq!(memo.call(10), 55);
assert_eq!(memo.call(30), 832040);
}
}
(* 1051: Fibonacci with HashMap Memoization *)
(* Approach 1: Recursive with Hashtbl memoization *)
let fib_memo () =
let cache = Hashtbl.create 64 in
let rec fib n =
if n <= 1 then n
else
match Hashtbl.find_opt cache n with
| Some v -> v
| None ->
let v = fib (n - 1) + fib (n - 2) in
Hashtbl.add cache n v;
v
in
fib
(* Approach 2: Functional memoization with ref to Map *)
module IntMap = Map.Make(Int)
let fib_map n =
let cache = ref IntMap.empty in
let rec fib n =
if n <= 1 then n
else
match IntMap.find_opt n !cache with
| Some v -> v
| None ->
let v = fib (n - 1) + fib (n - 2) in
cache := IntMap.add n v !cache;
v
in
fib n
(* Approach 3: CPS with memoization *)
let fib_cps n =
let cache = Hashtbl.create 64 in
let rec fib n k =
if n <= 1 then k n
else
match Hashtbl.find_opt cache n with
| Some v -> k v
| None ->
fib (n - 1) (fun a ->
fib (n - 2) (fun b ->
let v = a + b in
Hashtbl.add cache n v;
k v))
in
fib n Fun.id
let () =
let fib = fib_memo () in
assert (fib 0 = 0);
assert (fib 1 = 1);
assert (fib 10 = 55);
assert (fib 20 = 6765);
assert (fib 30 = 832040);
assert (fib_map 10 = 55);
assert (fib_map 20 = 6765);
assert (fib_map 30 = 832040);
assert (fib_cps 10 = 55);
assert (fib_cps 20 = 6765);
assert (fib_cps 30 = 832040);
Printf.printf "โ All tests passed\n"