【问题标题】:Solve Sequence Alignment using memoization in OCaml cannot get correct answer在 OCaml 中使用 memoization 解决序列比对无法得到正确答案
【发布时间】:2014-12-08 10:36:10
【问题描述】:

我使用 Hashtbl 存储(int*string*string),这是问题的答案。但是,当我运行时,答案不正确。我认为它跳过了一些值并且没有计算它们。

这是我的代码:

let solve_memo (x:string) (y:string) : (int*string*string)  =
    let x_len = String.length x in
    let y_len = String.length y in
    let tbl_size = x_len * y_len in
    let tbl = Hashtbl.create tbl_size in
    let case1 xi yi =
        if x.[xi] = y.[yi] then 1 else -1
    in
    let choose_max (pt1,strxi,stryi) (pt2,strx2,stry2) (pt3,strx3,stry3) =
        if pt1>pt2 then
            if pt1>pt3 then (pt1,strxi,stryi)
            else (pt3,strx3,stry3)
        else 
            if pt2>pt3 then (pt2,strx2,stry2) 
            else (pt3,strx3,stry3)
    in
    let rec aux xi yi (pt,strx,stry) =
    if (xi = x_len && yi = y_len) then (pt,strx,stry)
    else
    if xi = x_len then 
        (
            pt + (-2 * (y_len - yi)),
            String.concat "" [strx; String.make (y_len - yi) ' '],
            String.concat "" [stry; String.sub y (yi) (y_len - yi)]
        )
    else if yi = y_len then
        (pt + (-2 * (x_len - xi)),
        String.concat "" [strx; String.sub x (xi) (x_len - xi)],
        String.concat "" [stry; String.make (x_len - xi) ' ']
        )
    else 
        try
        Hashtbl.find tbl (xi,yi)
        with _ ->
        let r1 = aux (xi+1) (yi+1) (pt+(case1 xi yi), String.concat "" [strx; Char.escaped x.[xi]] , String.concat "" [stry; Char.escaped y.[yi]]) in
        let r2 = aux (xi+1) yi ((pt-2), String.concat "" [strx; Char.escaped x.[xi]] , String.concat "" [stry; " "]) in
        let r3 = aux xi (yi+1) ((pt-2), String.concat "" [strx; " "] , String.concat "" [stry; Char.escaped y.[yi]]) in
        let r = choose_max r1 r2 r3 in
        let _ = Hashtbl.add tbl (xi,yi) r in
        r
in aux 0 0 (0,"","")
;;

当我打印在 hashtbl 中找到的值时,它会重复 2-3 个可能在迭代开始时创建的值。 谢谢。

添加:

这是原始的简单求解函数,我想添加一个 Hashtbl 以使其更快,但它返回错误的结果

let solve (x:string) (y:string) : (int*string*string)  =
let x_len = String.length x in
let y_len = String.length y in
let rec aux xi yi (pt,strx,stry) =
let case1 xi yi =
    if x.[xi] = y.[yi] then 1 else -1
    in
    let choose_max (pt1,strxi,stryi) (pt2,strx2,stry2) (pt3,strx3,stry3) =
        if pt1>pt2 then
            if pt1>pt3 then (pt1,strxi,stryi)
            else (pt3,strx3,stry3)
        else 
            if pt2>pt3 then (pt2,strx2,stry2) 
            else (pt3,strx3,stry3)
    in
    if xi = x_len then 
        (
            pt + (-2 * (y_len - yi)),
            String.concat "" [strx; String.make (y_len - yi) ' '],
            String.concat "" [stry; String.sub y (yi) (y_len - yi)]
        )
    else if yi = y_len then
        (pt + (-2 * (String.length x - xi)),
        String.concat "" [strx; String.sub x (xi) (x_len - xi)],
        String.concat "" [stry; String.make (x_len - xi) ' ']
        )
    else
        let r1 = aux (xi+1) (yi+1) (pt+(case1 xi yi), String.concat "" [strx; Char.escaped x.[xi]] , String.concat "" [stry; Char.escaped y.[yi]]) in
        let r2 = aux (xi+1) yi ((pt-2), String.concat "" [strx; Char.escaped x.[xi]] , String.concat "" [stry; " "]) in
        let r3 = aux xi (yi+1) ((pt-2), String.concat "" [strx; " "] , String.concat "" [stry; Char.escaped y.[yi]]) in
        choose_max r1 r2 r3
in aux 0 0 (0,"","")
;;

【问题讨论】:

  • 我想向你保证,OCaml 没有损坏。我们每天都在对其进行测试))尝试重写您的代码,使其至少易于理解。
  • 这是解决问题的一种奇怪方法。您应该考虑每个子字符串是一个矩阵,并记住每个子矩阵(子字符串对)的结果。您在矩阵中向前看,而不是向后看子问题。我建议您解决一个单元格,因为邻居已被记忆,然后在 hashtbl 中记忆该结果并返回。有了这个基本的直觉,您可以看到您应该查询包含答案 (x_len,y_len) 的单元格。
  • 此外,存储定义对齐方式的对将占用足够大的序列的内存。通过矩阵的回溯是 O(n) 阶,因此不会增加填充成本和对齐方向 (O(n*n)) 的复杂性。
  • 虽然,不是您正在寻找的递归解决方案,但回溯算法和成本函数的隔离包含在 phylocaml 项目 (github.com/amnh/phylocaml)alignment.ml 文件中在 FullAlign 模块中。这是功能化的,因此无法满足您的需求,但会为您正在寻找的解决方案的简单性提供一些指导。
  • 感谢您的 cmets。我最近才开始学习 OCaml 和函数式编程。我还有很多东西要学。我添加了一些信息,但我会尝试使用您的建议进行修复。

标签: dynamic recursion ocaml dynamic-programming memoization


【解决方案1】:

我认为您无法轻松修改此函数以获取 hashtbl 进行记忆。这是因为您从三个不同的方向(n0..n2,在下图中)将累加器 (pt,strx,stry) 传递到单元格 (xi,yi)(累加器表示子序列的对齐)。在与序列的其余部分对齐之前,您尚未确定这三个中的哪一个是最佳的 (r0..r2)。

通过添加 memoization,您只存储了一次此类调用的结果,而不是最佳结果。因此,您的结果取决于先前的单元格(来自n0..n2 的累加器)以及对序列其余部分的最佳对齐的重新生成r0..r2。正因为如此,您无法在不详尽的情况下记住结果。要使记忆化工作,您需要明确的子问题,而这些子问题在这里没有。

【讨论】:

  • 非常感谢!它正在通过导致此问题的累加器。我以为我会使用累加器存储值以供以后输出,但不知何故它与使用 Hashtbl 混淆了。
  • 我解决这个问题的方法是删除辅助递归函数中的累加器,并稍微改变基本情况和递归调用以适应新函数
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 1970-01-01
  • 2016-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多