为什么我的子函数会有一个指数级的调用?

时间:2018-04-28 16:49:59

标签: haskell recursion lambda exponential

我遇到以下Haskell函数的问题:

evalPol :: Float
        -> Float
        -> Integer
        -> Integer
        -> (s -> a -> [(s, [(Float, Float)])])
        -> (s -> a)
        -> [s]
        -> [((s -> Float), Float)]
        -> [((s -> Float), Float)]
evalPol eps gamma maxIter nIter gen pol ss vofss =
  if nIter >= maxIter || delta < eps
    then reverse ((vofs', delta) : vofss)
    else evalPol eps gamma maxIter (nIter + 1) gen pol ss ((vofs', delta) : vofss)
 where delta   = maximum [abs (vofs s - vofs' s) | s <- ss]
       vofs' s = actVal gamma gen vofs s (pol s)
       vofs    = (fst . P.head) vofss

如果我用maxIter = 1调用此函数并运行分析,那么我会看到函数入口计数,这对我来说很有意义:

evalPol..............2
  evalPol.delta......1
    evalPol.vofs'..441

上面的函数入口计数对我有意义,如下所示:

  1. evalPol输入两次:

    1. 一次,从外面打电话,
    2. 一次,递归。 (由于:maxIter = 1,因此只允许一次递归调用。)
  2. evalPol.delta只被调用一次,因为当第二次(递归地)调用evalPol时,测试:nIter >= maxIter成功,并且不需要评估{{} 1}}。

  3. 有意义的是我在delta中获得了441个条目,因为我将该函数映射到列表evalPol.vofs'上,并且该列表中有441个项目。

  4. 现在,如果我只进行一次更改:使用ss调用evalPol,我发现我的程序不会在合理的时间内终止。 允许它在中断之前运行几个小时,我得到以下内容:

    maxIter = 2

    evalPol................2 evalPol.delta........2 evalPol.vofs'..60366 的条目数从1变为2(参见上面的#2。)对我来说很有意义,因为我设置了evalPol.delta。 但是,我期望maxIter = 2的条目数量大幅增加。 (我期待看到882个条目,每个允许递归441个。) 在evalPol.vofs'中,evalPol.vofs'中的条目数似乎是指数。 (我不知道这个,因为我没有让程序结束。)

    如果我“展开”这2个递归的情况,寻找maxIter的指数依赖,我得到:

    maxIter

    我看到正如预期的那样开发递归,但我没有看到任何嵌套调用-- This is how I call it from outside: evalPol eps gamma 2 0 gen pol ss [((const 0), 0)] = -- Assume delta >= eps and recurse. evalPol eps gamma 2 1 gen pol ss [(vofs', delta), ((const 0), 0)] -- Now, expand delta delta = maximum $ map (abs . uncurry (-) . (vofs &&& vofs')) ss -- Substitute for vofs, vofs', and pol, using previous iteration's definitions. = maximum $ map ( abs . uncurry (-) . ( vofs' &&& \s -> actVal gamma gen vofs' s 0 ) ) ss = maximum $ map ( abs . uncurry (-) . ( \s -> actVal gamma gen (const 0) s 0 &&& \s -> actVal gamma gen (\s' -> actVal gamma gen (const 0) s' 0) s 0 ) ) ss ,这可能解释了我所观察到的evalPol.vofs'的(假设的)指数依赖。 此外,我查看了maxIter函数以及actVal函数,并且没有调用gen隐藏在其中任何一个函数中。 所以,我无法向evalPol.vofs'案例中的evalPol.vofs'解释这些大量的条目。

1 个答案:

答案 0 :(得分:1)

我通过使用vofs'函数的向量表示来解决这个问题。 这样做已经消除了之前执行的冗余计算。我现在看到882次调用新的等效vofs',对于 2递归情况,这是我所期望的。也就是说,我希望evalPolmaxIter的执行时间为线性,并使用vofs'的向量表示,这就是我所看到的。