Performance 求解F#中的背包问题:性能
我发现一篇文章:Performance 求解F#中的背包问题:性能,performance,optimization,f#,knapsack-problem,Performance,Optimization,F#,Knapsack Problem,我发现一篇文章: 关于背包问题在F#中的实现。在我学习这门语言的过程中,我发现这真的很有趣,并尝试对此进行一些研究。以下是我编写的代码: open System open System.IO open System.Collections.Generic let parseToTuple (line : string) = let parsedLine = line.Split(' ') |> Array.filter(not << String.IsNullOr
关于背包问题在F#中的实现。在我学习这门语言的过程中,我发现这真的很有趣,并尝试对此进行一些研究。以下是我编写的代码:
open System
open System.IO
open System.Collections.Generic
let parseToTuple (line : string) =
let parsedLine = line.Split(' ') |> Array.filter(not << String.IsNullOrWhiteSpace) |> Array.map Int32.Parse
(parsedLine.[0], parsedLine.[1])
let memoize f =
let cache = Dictionary<_, _>()
fun x ->
if cache.ContainsKey(x)
then cache.[x]
else
let res = f x
cache.[x] <- res
res
type Item =
{
Value : int
Size : int
}
type ContinuationBuilder() =
member b.Bind(x, f) = fun k -> x (fun x -> f x k)
member b.Return x = fun k -> k x
member b.ReturnFrom x = x
let cont = ContinuationBuilder()
let set1 =
[
(4, 11)
(8, 4)
(10, 5)
(15, 8)
(4, 3)
]
let set2 =
[
(50, 341045); (1906, 4912); (41516, 99732); (23527, 56554); (559, 1818); (45136, 108372); (2625, 6750); (492, 1484)
(1086, 3072); (5516, 13532); (4875, 12050); (7570, 18440); (4436, 10972); (620, 1940); (50897, 122094); (2129, 5558)
(4265, 10630); (706, 2112); (2721, 6942); (16494, 39888); (29688, 71276); (3383, 8466); (2181, 5662); (96601, 231302)
(1795, 4690); (7512, 18324); (1242, 3384); (2889, 7278); (2133, 5566); (103, 706); (4446, 10992); (11326, 27552)
(3024, 7548); (217, 934); (13269, 32038); (281, 1062); (77174, 184848); (952, 2604); (15572, 37644); (566, 1832)
(4103, 10306); (313, 1126); (14393, 34886); (1313, 3526); (348, 1196); (419, 1338); (246, 992); (445, 1390)
(23552, 56804); (23552, 56804); (67, 634)
]
[<EntryPoint>]
let main args =
// prepare list of items from a file args.[0]
let header, items = set1
|> function
| h::t -> h, t
| _ -> raise (Exception("Wrong data format"))
let N, K = header
printfn "N = %d, K = %d" N K
let items = List.map (fun x -> {Value = fst x ; Size = snd x}) items |> Array.ofList
let rec combinations =
let innerSolver key =
cont
{
match key with
| (i, k) when i = 0 || k = 0 -> return 0
| (i, k) when items.[i-1].Size > k -> return! combinations (i-1, k)
| (i, k) -> let item = items.[i-1]
let! v1 = combinations (i-1, k)
let! beforeItem = combinations (i-1, k-item.Size)
let v2 = beforeItem + item.Value
return max v1 v2
}
memoize innerSolver
let res = combinations (N, K) id
printfn "%d" res
0
开放系统
开放系统
open System.Collections.Generic
让parseToTuple(行:string)=
让parsedLine=line.Split(“”)|>Array.filter(而不是Array.map Int32.Parse
(解析线[0],解析线[1])
让我们回忆一下f=
让缓存=字典()
乐趣x->
if cache.ContainsKey(x)
然后缓存
其他的
设res=fx
缓存.x]x(乐趣x->f x k)
成员b.返回x=fun k->k x
成员b.ReturnFrom x=x
设cont=ContinuationBuilder()
让set1=
[
(4, 11)
(8, 4)
(10, 5)
(15, 8)
(4, 3)
]
让set2=
[
(50, 341045); (1906, 4912); (41516, 99732); (23527, 56554); (559, 1818); (45136, 108372); (2625, 6750); (492, 1484)
(1086, 3072); (5516, 13532); (4875, 12050); (7570, 18440); (4436, 10972); (620, 1940); (50897, 122094); (2129, 5558)
(4265, 10630); (706, 2112); (2721, 6942); (16494, 39888); (29688, 71276); (3383, 8466); (2181, 5662); (96601, 231302)
(1795, 4690); (7512, 18324); (1242, 3384); (2889, 7278); (2133, 5566); (103, 706); (4446, 10992); (11326, 27552)
(3024, 7548); (217, 934); (13269, 32038); (281, 1062); (77174, 184848); (952, 2604); (15572, 37644); (566, 1832)
(4103, 10306); (313, 1126); (14393, 34886); (1313, 3526); (348, 1196); (419, 1338); (246, 992); (445, 1390)
(23552, 56804); (23552, 56804); (67, 634)
]
[]
让主参数=
//准备文件args中的项目列表。[0]
let header,items=set1
|>作用
|h::t->h,t
|->raise(异常(“错误数据格式”))
设N,K=header
printfn“N=%d,K=%d”N K
让items=List.map(funx->{Value=fstx;Size=sndx})items}>Array.ofList
let rec组合=
让innerSolver键=
续
{
匹配键
|(i,k)当i=0 | | k=0时->返回0
|(i,k)当项目[i-1]。大小>k->返回!组合(i-1,k)
|(i,k)->设项=项。[i-1]
设!v1=组合(i-1,k)
let!beforeItem=组合(i-1,k-item.Size)
设v2=beforeItem+item.Value
返回最大v1 v2
}
记忆内部解算器
设res=组合(N,K)id
printfn“%d”res
0
然而,这个实现的问题是速度非常慢(实际上,我无法解决50个项目和大约300000个容量的问题,这是我用C#在不到1s的时间内简单实现的)
你能告诉我我是否在某个地方犯了错误吗?或者可能实现是正确的,这只是解决此问题的低效方法。当你天真地应用这样一个通用的回忆体,并使用延续传递时,回忆体缓存中的值是延续,而不是常规的“最终值”结果。因此,当你得到一个缓存命中时,你没有得到一个最终的结果,你得到的是一个函数,该函数承诺在你调用它时计算结果。这个调用可能会很昂贵,可能会调用其他各种延续,可能最终会再次命中记忆缓存本身,等等 有效地记忆连续传递函数,这样a)缓存工作完全有效,b)函数保持尾部递归是相当困难的。阅读讨论,当你完全理解它时再回来 你链接的博客文章的作者正在使用一种更复杂、更不通用的回忆录,它特别适合这个问题。诚然,我还没有完全研究它(博客上的代码不完整/不完整,很难尝试),但我认为它的要点是,它在缓存最终整数结果之前“强制”连续链 为了说明这一点,这里有一个代码的快速重构,它是完全自包含的,并跟踪相关信息:
open System
open System.Collections.Generic
let mutable cacheHits = 0
let mutable cacheMisses = 0
let memoize f =
let cache = Dictionary<_, _>()
fun x ->
match cache.TryGetValue(x) with
| (true, v) ->
cacheHits <- cacheHits + 1
printfn "Hit for %A - Result is %A" x v
v
| _ ->
cacheMisses <- cacheMisses + 1
printfn "Miss for %A" x
let res = f x
cache.[x] <- res
res
type Item = { Value : int; Size : int }
type ContinuationBuilder() =
member b.Bind(x, f) = fun k -> x (fun x -> f x k)
member b.Return x = fun k -> k x
member b.ReturnFrom x = x
let cont = ContinuationBuilder()
let genItems n =
[| for i = 1 to n do
let size = i % 5
let value = (size * i)
yield { Value = value; Size = size }
|]
let N, K = (5, 100)
printfn "N = %d, K = %d" N K
let items = genItems N
let rec combinations_cont =
memoize (
fun key ->
cont {
match key with
| (0, _) | (_, 0) -> return 0
| (i, k) when items.[i-1].Size > k -> return! combinations_cont (i - 1, k)
| (i, k) -> let item = items.[i-1]
let! v1 = combinations_cont (i-1, k)
let! beforeItem = combinations_cont (i-1, k - item.Size)
let v2 = beforeItem + item.Value
return max v1 v2
}
)
let res = combinations_cont (N, K) id
printfn "Answer: %d" res
printfn "Memo hits: %d" cacheHits
printfn "Memo misses: %d" cacheMisses
printfn ""
let rec combinations_plain =
memoize (
fun key ->
match key with
| (i, k) when i = 0 || k = 0 -> 0
| (i, k) when items.[i-1].Size > k -> combinations_plain (i-1, k)
| (i, k) -> let item = items.[i-1]
let v1 = combinations_plain (i-1, k)
let beforeItem = combinations_plain (i-1, k-item.Size)
let v2 = beforeItem + item.Value
max v1 v2
)
cacheHits <- 0
cacheMisses <- 0
let res2 = combinations_plain (N, K)
printfn "Answer: %d" res2
printfn "Memo hits: %d" cacheHits
printfn "Memo misses: %d" cacheMisses
开放系统
open System.Collections.Generic
设可变缓存命中数=0
设可变缓存未命中=0
让我们回忆一下f=
让缓存=字典()
乐趣x->
将cache.TryGetValue(x)与
|(对,v)->
缓存命中
缓存未命中(f x k)
成员b.返回x=fun k->k x
成员b.ReturnFrom x=x
设cont=ContinuationBuilder()
设genitem n=
[|对于i=1到n do
让大小=i%5
让值=(大小*i)
产量{Value=Value;Size=Size}
|]
设N,K=(5100)
printfn“N=%d,K=%d”N K
设items=genItems N
让记录组合\u cont=
回忆(
趣味键->
续{
匹配键
|(0,124;(124;,0)->返回0
|(i,k)当项目[i-1].大小>k->返回!组合\u cont(i-1,k)
|(i,k)->设项=项。[i-1]
设!v1=组合(i-1,k)
let!beforeItem=组合(i-1,k-项目大小)
设v2=beforeItem+item.Value
返回最大v1 v2
}
)
设res=cont(N,K)id的组合
printfn“答案:%d”res
printfn“备忘录命中率:%d”缓存命中率
printfn“备忘录未命中:%d”缓存未命中
printfn
open System
open System.Diagnostics
open System.Collections.Generic
let time f =
System.GC.Collect()
let sw = Stopwatch.StartNew()
let r = f()
sw.Stop()
printfn "Took: %f" sw.Elapsed.TotalMilliseconds
r
let mutable cacheHits = 0
let mutable cacheMisses = 0
let memoize f =
let cache = Dictionary<_, _>()
fun x ->
match cache.TryGetValue(x) with
| (true, v) ->
cacheHits <- cacheHits + 1
//printfn "Hit for %A - Result is %A" x v
v
| _ ->
cacheMisses <- cacheMisses + 1
//printfn "Miss for %A" x
let res = f x
cache.[x] <- res
res
type Item = { Value : int; Size : int }
type ContinuationBuilder() =
member b.Bind(x, f) = fun k -> x (fun x -> f x k)
member b.Return x = fun k -> k x
member b.ReturnFrom x = x
let cont = ContinuationBuilder()
let genItems n =
[| for i = 1 to n do
let size = i % 5
let value = (size * i)
yield { Value = value; Size = size }
|]
let N, K = (80, 400)
printfn "N = %d, K = %d" N K
let items = genItems N
//let rec combinations_cont =
// memoize (
// fun key ->
// cont {
// match key with
// | (0, _) | (_, 0) -> return 0
// | (i, k) when items.[i-1].Size > k -> return! combinations_cont (i - 1, k)
// | (i, k) -> let item = items.[i-1]
// let! v1 = combinations_cont (i-1, k)
// let! beforeItem = combinations_cont (i-1, k - item.Size)
// let v2 = beforeItem + item.Value
// return max v1 v2
// }
// )
//
//
//cacheHits <- 0
//cacheMisses <- 0
//let res = time(fun () -> combinations_cont (N, K) id)
//printfn "Answer: %d" res
//printfn "Memo hits: %d" cacheHits
//printfn "Memo misses: %d" cacheMisses
//printfn ""
let rec combinations_plain =
memoize (
fun key ->
match key with
| (i, k) when i = 0 || k = 0 -> 0
| (i, k) when items.[i-1].Size > k -> combinations_plain (i-1, k)
| (i, k) -> let item = items.[i-1]
let v1 = combinations_plain (i-1, k)
let beforeItem = combinations_plain (i-1, k-item.Size)
let v2 = beforeItem + item.Value
max v1 v2
)
cacheHits <- 0
cacheMisses <- 0
printfn "combinations_plain"
let res2 = time (fun () -> combinations_plain (N, K))
printfn "Answer: %d" res2
printfn "Memo hits: %d" cacheHits
printfn "Memo misses: %d" cacheMisses
printfn ""
let recursivelyMemoize f =
let cache = Dictionary<_, _>()
let rec memoizeAux x =
match cache.TryGetValue(x) with
| (true, v) ->
cacheHits <- cacheHits + 1
//printfn "Hit for %A - Result is %A" x v
v
| _ ->
cacheMisses <- cacheMisses + 1
//printfn "Miss for %A" x
let res = f memoizeAux x
cache.[x] <- res
res
memoizeAux
let combinations_plain2 =
let combinations_plain2Aux combinations_plain2Aux key =
match key with
| (i, k) when i = 0 || k = 0 -> 0
| (i, k) when items.[i-1].Size > k -> combinations_plain2Aux (i-1, k)
| (i, k) -> let item = items.[i-1]
let v1 = combinations_plain2Aux (i-1, k)
let beforeItem = combinations_plain2Aux (i-1, k-item.Size)
let v2 = beforeItem + item.Value
max v1 v2
let memoized = recursivelyMemoize combinations_plain2Aux
fun x -> memoized x
cacheHits <- 0
cacheMisses <- 0
printfn "combinations_plain2"
let res3 = time (fun () -> combinations_plain2 (N, K))
printfn "Answer: %d" res3
printfn "Memo hits: %d" cacheHits
printfn "Memo misses: %d" cacheMisses
printfn ""
let recursivelyMemoizeCont f =
let cache = Dictionary HashIdentity.Structural
let rec memoizeAux x k =
match cache.TryGetValue(x) with
| (true, v) ->
cacheHits <- cacheHits + 1
//printfn "Hit for %A - Result is %A" x v
k v
| _ ->
cacheMisses <- cacheMisses + 1
//printfn "Miss for %A" x
f memoizeAux x (fun y ->
cache.[x] <- y
k y)
memoizeAux
let combinations_cont2 =
let combinations_cont2Aux combinations_cont2Aux key =
cont {
match key with
| (0, _) | (_, 0) -> return 0
| (i, k) when items.[i-1].Size > k -> return! combinations_cont2Aux (i - 1, k)
| (i, k) -> let item = items.[i-1]
let! v1 = combinations_cont2Aux (i-1, k)
let! beforeItem = combinations_cont2Aux (i-1, k - item.Size)
let v2 = beforeItem + item.Value
return max v1 v2
}
let memoized = recursivelyMemoizeCont combinations_cont2Aux
fun x -> memoized x id
cacheHits <- 0
cacheMisses <- 0
printfn "combinations_cont2"
let res4 = time (fun () -> combinations_cont2 (N, K))
printfn "Answer: %d" res4
printfn "Memo hits: %d" cacheHits
printfn "Memo misses: %d" cacheMisses
printfn ""
N = 80, K = 400
combinations_plain
Took: 7.191000
Answer: 6480
Memo hits: 6231
Memo misses: 6552
combinations_plain2
Took: 6.310800
Answer: 6480
Memo hits: 6231
Memo misses: 6552
combinations_cont2
Took: 17.021200
Answer: 6480
Memo hits: 6231
Memo misses: 6552