Haskell 为什么子函数的调用次数是指数级的?
我对以下Haskell函数有问题:Haskell 为什么子函数的调用次数是指数级的?,haskell,recursion,lambda,exponential,Haskell,Recursion,Lambda,Exponential,我对以下Haskell函数有问题: evalPol :: Float -> Float -> Integer -> Integer -> (s -> a -> [(s, [(Float, Float)])]) -> (s -> a) -> [s] -> [((s -> Float), Float)] -&
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
上面的函数条目计数对我来说有意义,如下所示:
evalPol
输入两次:
maxIter=1
,只允许一个递归调用)evalPol.delta
只被调用一次,因为当第二次(递归地)调用evalPol
时,测试:nIter>=maxIter
成功,并且不需要计算delta
evalPol.vofs'
中得到441个条目是有意义的,因为我将该函数映射到列表ss
,该列表中有441个条目maxIter=2
调用evalPol
,我发现我的程序没有在合理的时间内终止。
在中断之前让它运行几个小时,我得到以下结果:
evalPol................2
evalPol.delta........2
evalPol.vofs'..60366
将evalPol.delta
的条目数从1改为2(见上文#2)对我来说很有意义,因为我已经设置了maxIter=2
。
然而,我没有料到evalPol.vofs'
中的条目数量会如此激增。
(我希望看到882个条目,每个允许的递归441个。)
在maxIter
中,evalPol.vofs'
的条目数似乎是指数级的。
(我不知道这一点,因为我没有让程序结束。)
如果我“展开”这个2递归案例,寻找对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'
。
因此,我无法解释我在
maxIter=2
案例中观察到的evalPol.vofs'
中的大量条目。我通过使用vofs'
函数的向量表示来解决这个问题。
这样做消除了以前执行的冗余计算。现在,对于2个递归,我看到了882个对新等价的
vofs'
的调用,这正是我所期望的。也就是说,我希望evalPol
的执行时间在maxIter
中是线性的,使用vofs'
的向量表示,这就是我看到的。请创建一个。如果没有MCVE,对其进行基准测试和尝试替代方案需要付出太多的努力。是的,将代码缩减到仍然产生此问题的最小示例。否则,“为什么这个代码不起作用?”是投票结束主题时的一个选项;当问题变得如此具体时,问答的普遍适用性会受到严重限制,调试的工作量也会变得很高。我怀疑如果不采用尾部递归的方式,这会大大简化。不过,您应该编辑以包含所有相关细节,然后发布您自己的答案并接受它。就是这样。这些问答项目旨在为整个社区服务,而不仅仅是询问者。:)
-- 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