我有以下具有指数复杂性的函数:
c :: Integer -> Integer
c n
| n <= 4 = n + 10
| otherwise = c (n-1) + 2 * (c (n-4))
我正在努力使这个功能的复杂性变为线性。
c x
即使1000&lt; x&lt; 100000
答案 0 :(得分:5)
至少有两种方法能够及时线性地解决这个问题。
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]
这里我们使用四个中间寄存器,并在go
循环期间移位它们。我们可以使用元组(a, b, c, d)
而不是列表,但是从这里开始映射会更方便。
此解决方案在空间上是恒定的,并且在时间上是线性的。
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
这里我们使用Haskell的懒惰(正常评估策略+ memoization)。无限列表results
根据需要逐个生成值。它被c2
函数用作数据,它只是请求生成器n
- 数字和自定义。同时,在需要之前,该数据不存在。这类数据称为 codata ,在Haskell中很常见。
此解决方案在空间和时间上是线性的。
两种解决方案都处理负数和大正数。