Haskell 函数复杂性从指数到线性

Haskell 函数复杂性从指数到线性,haskell,time-complexity,Haskell,Time Complexity,我有以下具有指数复杂性的函数: c :: Integer -> Integer c n | n <= 4 = n + 10 | otherwise = c (n-1) + 2 * (c (n-4)) c::Integer->Integer 碳氮 |n至少有两种方法可以及时线性地解决这个问题 使用中间存储器 这里我们使用Haskell的惰性(正常评估策略+记忆)。无限列表结果根据需要逐个生成值。它被c2函数用作数据,该函数仅请求生成器输入n-第个数字,并用于自定义。同时

我有以下具有指数复杂性的函数:

c :: Integer -> Integer
c n 
   | n <= 4 = n + 10
   | otherwise = c (n-1) + 2 * (c (n-4))
c::Integer->Integer
碳氮

|n至少有两种方法可以及时线性地解决这个问题

使用中间存储器 这里我们使用Haskell的惰性(正常评估策略+记忆)。无限列表
结果
根据需要逐个生成值。它被
c2
函数用作数据,该函数仅请求生成器输入
n
-第个数字,并用于自定义。同时,这些数据直到需要时才存在。这种类型的数据称为codata,在Haskell中相当常见

这个解在空间和时间上是线性的


这两种解决方案都处理负数和大正数。

至少有两种方法可以在时间上线性地解决这个问题

使用中间存储器 这里我们使用Haskell的惰性(正常评估策略+记忆)。无限列表
结果
根据需要逐个生成值。它被
c2
函数用作数据,该函数仅请求生成器输入
n
-第个数字,并用于自定义。同时,这些数据直到需要时才存在。这种类型的数据称为codata,在Haskell中相当常见

这个解在空间和时间上是线性的


这两种解决方案都处理负数和大正数。

请查看。您可以从
0
开始,将该函数的值制成表格,并使用此表格上的查找替换递归调用。使用一个简单的列表,这将变成O(n^2)。正如n.m.所建议的那样,这可以以一种避免O(n)查找并实现O(n)总复杂性的方式发展。这是的半重复。请看一下。您可以从
0
开始将该函数的值制成表格,并用此表上的查找替换递归调用。使用一个简单的列表,这将变成O(n^2)。正如n.m.所建议的,这可以以一种避免O(n)查找并实现O(n)总复杂性的方式发展。这是的半重复。另一种方法是用矩阵乘法表示递归。也就是说,给定序列中四个连续元素的列向量,您可以在左侧将它们与固定矩阵相乘,得到序列中的下四个元素<代码>m*m*m*..*v
。但这只是
m^k*v
,使用平方求幂(
stimesMonoid
),这种计算在实践中比
c1
@dfeuer更有效,这确实是一种非常好的递归公式的数学方法,既优雅又实用。在一般非数值(或非线性)情况下,迭代循环或记忆仍然有用。另一种方法是用矩阵乘法表示递归。也就是说,给定序列中四个连续元素的列向量,您可以在左侧将它们与固定矩阵相乘,得到序列中的下四个元素<代码>m*m*m*..*v。但这只是
m^k*v
,使用平方求幂(
stimesMonoid
),这种计算在实践中比
c1
@dfeuer更有效,这确实是一种非常好的递归公式的数学方法,既优雅又实用。在一般非数值(或非线性)情况下,迭代循环或记忆仍然有用。
c1 :: Integer -> Integer
c1 n 
  | n <= 4 = n + 10
  | otherwise = go n (map c1 [4,3,2,1])
  where
    go 4 (a:_) = a 
    go n [a,b,c,d] = go (n-1) [a + 2*d, a, b, c]
c2 :: Integer -> Integer
c2 n
  | n <= 4 = n + 10
  | otherwise = results !! fromInteger (n - 1)
  where
    results = [11..14] ++ zipWith f (drop 3 results) results 
    f a b = a + 2*b