Haskell 这个函数的计算复杂度是O(2^n)还是O(n)

Haskell 这个函数的计算复杂度是O(2^n)还是O(n),haskell,Haskell,我想做一个函数,创建一个无限列表,它以两个数字和一个运算符作为输入,这样它就可以生成算术和几何序列 infiniteList:: (Floating a)=>a->(a->a->a)->a->[a] infiniteList start operation changeby = start:[(operation x changeby)| x<-(infiniteList start operation changeby)] infiniteLi

我想做一个函数,创建一个无限列表,它以两个数字和一个运算符作为输入,这样它就可以生成算术和几何序列

infiniteList:: (Floating a)=>a->(a->a->a)->a->[a]
infiniteList start operation changeby = 
  start:[(operation x changeby)| x<-(infiniteList start operation changeby)]
infiniteList::(浮动a)=>a->(a->a->a)->a->[a]
无限列表启动操作更改依据=

开始:[(x changeby操作)|x我不确定你是从哪里得到“批量”的想法的。下面是列表中前几个元素的转录本。从这一点上,我认为你应该能够理解复杂性

列表的第一个元素是什么?它是
start
,因为
infiniteList
被定义为
start:[something]
,而任何形式的列表的第一个元素都是
start

列表中的第二个元素是什么?我们当然需要查阅
[something]
上面列表的一部分。该子列表的第一个元素是
操作x changeby
,其中
x
无限列表
的第一个元素。我们已经确定第一个元素是
开始
,所以第二个元素是
操作开始变更,这正是我们想要的。我们应该怎么做我们必须计算得到第二个元素?只有第一个元素,再加上
运算

列表的第三个元素是什么?它是
[something]
的第二个元素,即
操作x changeby
,其中
x
无限列表
的第二个元素。幸运的是,我们刚刚计算了它是什么。。。 我们需要计算什么才能得到第三个元素?只有第一个和第二个元素,再加上
运算


虽然它不能直接回答这个问题,但您应该问问自己,您希望函数具有多大的复杂性。要获得
n
th元素,需要做多少工作?您在代码中的实现可能更差,但这可能有助于您以不同的方式思考代码。

我不确定您的目标是什么下面是列表中前几个元素的转录本。从中,我认为您应该能够了解复杂性

列表的第一个元素是什么?它是
start
,因为
infiniteList
被定义为
start:[something]
,而任何形式的列表的第一个元素都是
start

列表中的第二个元素是什么?我们当然需要查阅
[something]
上面列表的一部分。该子列表的第一个元素是
操作x changeby
,其中
x
无限列表
的第一个元素。我们已经确定第一个元素是
开始
,所以第二个元素是
操作开始变更,这正是我们想要的。我们应该怎么做我们必须计算得到第二个元素?只有第一个元素,再加上
运算

列表的第三个元素是什么?它是
[something]
的第二个元素,即
操作x changeby
,其中
x
无限列表
的第二个元素。幸运的是,我们刚刚计算了它是什么。。。 我们需要计算什么才能得到第三个元素?只有第一个和第二个元素,再加上
运算


虽然它不能直接回答这个问题,但您应该问问自己,您希望函数具有多大的复杂性。要得到
n
th元素需要做多少工作?您在代码中的实现可能更差,但它可能会帮助您以不同的方式思考代码。

只需做一些数学运算,假设计算第n项需要
T(n)
计算,如下所示

[(operation x changeby)| x<-(infiniteList start operation changeby)]

实际上,您可以通过运行一些示例来“感受”时间复杂性。让
fn=(无穷列表0(+)1)!!n
,然后运行
f10
f100
f1000
f10000
,您可以看到差异

通常,当
n=1000
在短时间内运行时,
n=10000
运行一段时间,比如1秒或2秒,并且
n=100000
永远运行时,通常是
O(n^2)


顺便说一句,有一种
O(n)
方法:

infi :: a -> (a -> a -> a) -> a -> [a]
infi x f s = x : infi (f x s) f s

你可以做一些数学运算并运行一些示例来感受差异。

只要做一些数学运算,假设计算第n项需要
T(n)
计算,如下所示

[(operation x changeby)| x<-(infiniteList start operation changeby)]

实际上,您可以通过运行一些示例来“感受”时间复杂性。让
fn=(无穷列表0(+)1)!!n
,然后运行
f10
f100
f1000
f10000
,您可以看到差异

通常,当
n=1000
在短时间内运行时,
n=10000
运行一段时间,比如1秒或2秒,并且
n=100000
永远运行时,通常是
O(n^2)


顺便说一句,有一种
O(n)
方法:

infi :: a -> (a -> a -> a) -> a -> [a]
infi x f s = x : infi (f x s) f s

你可以做一些数学运算并运行一些示例来感受差异。

一种有时对递归有用的策略是将其展开几次,以便更好地了解发生了什么。让我们尝试一下:

infiniteList start operation changeby = 
start:[(operation x changeby) | x <-
       start:[(operation x changeby) | x <-
              start:[(operation x changeby) | x <-
                     start:[(operation x changeby) | x <-
                            start:[(operation x changeby) | x <- (infiniteList start operation changeby)]]]]]

一种有时有助于递归的策略是将其展开几次,以便更好地了解发生了什么。让我们尝试一下:

infiniteList start operation changeby = 
start:[(operation x changeby) | x <-
       start:[(operation x changeby) | x <-
              start:[(operation x changeby) | x <-
                     start:[(operation x changeby) | x <-
                            start:[(operation x changeby) | x <- (infiniteList start operation changeby)]]]]]

关键工具是以下规则:

在分析绑定的性能(而不是总体性能)时,在分析其右侧时,允许您假设绑定本身已被完全评估

您正在定义
infiniteList
,因此允许您假设在RHS中,
infiniteList
绑定已被完全评估。不幸的是,这没有用,因为
infiniteList
只是一个函数,而完全评估它只会给出函数

但是你可以使用这个推理工具来解决问题:你必须绑定正确的东西

这显然是
O(n)

事实上,在第一个定义中

> infiniteList 1 (*) 2 !! 10000
> infiniteList 1 (*) 2 !! 10000
inf a f = a : [ f x | x <- inf a f ]
inf a f = let r = a : [ f x | x <- r ]
          in r