Yun*_*hen 24 scala memoization dynamic-programming
以下代码来自Pathikrit的动态编程存储库.我的美丽和独特使我感到困惑.
def subsetSum(s: List[Int], t: Int) = {
type DP = Memo[(List[Int], Int), (Int, Int), Seq[Seq[Int]]]
implicit def encode(key: (List[Int], Int)) = (key._1.length, key._2)
lazy val f: DP = Memo {
case (Nil, 0) => Seq(Nil)
case (Nil, _) => Nil
case (a :: as, x) => (f(as, x - a) map {_ :+ a}) ++ f(as, x)
}
f(s, t)
}
Run Code Online (Sandbox Code Playgroud)
该类型Memo在另一个文件中实现:
case class Memo[I <% K, K, O](f: I => O) extends (I => O) {
import collection.mutable.{Map => Dict}
val cache = Dict.empty[K, O]
override def apply(x: I) = cache getOrElseUpdate (x, f(x))
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:
为什么type K声明为(Int, Int)subsetSum?
什么是int在(Int, Int)分别代表?
3.如何(List[Int], Int)隐式转换为(Int, Int)?
我看不到implicit def foo(x:(List[Int],Int)) = (x._1.toInt,x._2).(甚至在Implicits.scala它导入的文件中也没有.
*编辑:好吧,我想念这个:
implicit def encode(key: (List[Int], Int)) = (key._1.length, key._2)
Run Code Online (Sandbox Code Playgroud)
我非常喜欢Pathikrit的图书馆scalgos.里面有很多Scala珍珠.请帮助我,这样我就能体会到Pathikrit的机智.谢谢.(:
pat*_*rit 55
我是上述代码的作者.
/**
* Generic way to create memoized functions (even recursive and multiple-arg ones)
*
* @param f the function to memoize
* @tparam I input to f
* @tparam K the keys we should use in cache instead of I
* @tparam O output of f
*/
case class Memo[I <% K, K, O](f: I => O) extends (I => O) {
import collection.mutable.{Map => Dict}
type Input = I
type Key = K
type Output = O
val cache = Dict.empty[K, O]
override def apply(x: I) = cache getOrElseUpdate (x, f(x))
}
object Memo {
/**
* Type of a simple memoized function e.g. when I = K
*/
type ==>[I, O] = Memo[I, I, O]
}
Run Code Online (Sandbox Code Playgroud)
在Memo[I <% K, K, O]:
I: input
K: key to lookup in cache
O: output
Run Code Online (Sandbox Code Playgroud)
该行I <% K意味着K可以从中查看(即隐式转换)I.
在大多数情况下,I应该是,K例如,如果您正在编写fibonacci类型函数,则可以单独Int => Int缓存Int.
但是,有时当你编写memoization时,你不想总是通过输入本身进行memoize或cache(I)而是输入()的函数,K例如当你编写subsetSum具有类型输入的算法时(List[Int], Int),你不想要使用List[Int]作为缓存的关键,而是你想使用List[Int].size作为缓存的关键组成部分.
所以,这是一个具体案例:
/**
* Subset sum algorithm - can we achieve sum t using elements from s?
* O(s.map(abs).sum * s.length)
*
* @param s set of integers
* @param t target
* @return true iff there exists a subset of s that sums to t
*/
def isSubsetSumAchievable(s: List[Int], t: Int): Boolean = {
type I = (List[Int], Int) // input type
type K = (Int, Int) // cache key i.e. (list.size, int)
type O = Boolean // output type
type DP = Memo[I, K, O]
// encode the input as a key in the cache i.e. make K implicitly convertible from I
implicit def encode(input: DP#Input): DP#Key = (input._1.length, input._2)
lazy val f: DP = Memo {
case (Nil, x) => x == 0 // an empty sequence can only achieve a sum of zero
case (a :: as, x) => f(as, x - a) || f(as, x) // try with/without a.head
}
f(s, t)
}
Run Code Online (Sandbox Code Playgroud)
您可以将所有这些缩短为一行:
type DP = Memo[(List[Int], Int), (Int, Int), Boolean]
对于常见情况(何时I = K),您可以简单地执行此操作:type ==>[I, O] = Memo[I, I, O]
并使用它来计算递归memoization 的二项式系数:
/**
* http://mathworld.wolfram.com/Combination.html
* @return memoized function to calculate C(n,r)
*/
val c: (Int, Int) ==> BigInt = Memo {
case (_, 0) => 1
case (n, r) if r > n/2 => c(n, n - r)
case (n, r) => c(n - 1, r - 1) + c(n - 1, r)
}
Run Code Online (Sandbox Code Playgroud)
要查看上述语法的详细信息,请参阅此问题.
这是一个完整的例子,它通过将输入的参数编码为:来计算editDistance:(Seq, Seq)(Seq.length, Seq.length)
/**
* Calculate edit distance between 2 sequences
* O(s1.length * s2.length)
*
* @return Minimum cost to convert s1 into s2 using delete, insert and replace operations
*/
def editDistance[A](s1: Seq[A], s2: Seq[A]) = {
type DP = Memo[(Seq[A], Seq[A]), (Int, Int), Int]
implicit def encode(key: DP#Input): DP#Key = (key._1.length, key._2.length)
lazy val f: DP = Memo {
case (a, Nil) => a.length
case (Nil, b) => b.length
case (a :: as, b :: bs) if a == b => f(as, bs)
case (a, b) => 1 + (f(a, b.tail) min f(a.tail, b) min f(a.tail, b.tail))
}
f(s1, s2)
}
Run Code Online (Sandbox Code Playgroud)
最后,规范的斐波那契例子:
lazy val fib: Int ==> BigInt = Memo {
case 0 => 0
case 1 => 1
case n if n > 1 => fib(n-1) + fib(n-2)
}
println(fib(100))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7637 次 |
| 最近记录: |