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

时间:2016-06-02 01:59:31

标签: haskell

我想创建一个创建无限列表的函数,该列表将两个数字和一个运算符作为输入,因此它可以生成算术和几何序列。

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

代码编译并正常工作:infiniteList 1 (*) 2生成一个从1开始的列表,后续数字是其前身的两倍。

现在我无法搞清楚计算复杂性&#34;计算列表的第n个元素&#34;。从技术上讲,它正在进行一项操作来确定列表中的每个元素。但是,如果您在(2 ^ k +1)项之后,我将不得不等待计算机首先完成计算2 ^(k + 1)个元素。

我希望我能正确解释这一点,所以基本上我认为该程序以2 ^ k批次生成元素,其中k是一个整数,所以你可能正在等待(2 ^(k + 1) - 2 ^ k)时间计算(2 ^ k +1)th整数。那么计算列表的第n个元素&#34;?

的计算复杂度是多少?

5 个答案:

答案 0 :(得分:7)

关键工具是以下规则:

  

在分析绑定的性能(而不是整体)时,您可以在分析其右侧时假设绑定本身已经完全评估。

您正在定义infiniteList,因此您可以假设在RHS中已完全评估infiniteList绑定。不幸的是,这没有用,因为infiniteList只是一个函数,而完全评估它只是给你功能!

但是你可以使用这个推理工具找出一个修复:你必须绑定正确的东西

infiniteList :: a -> (a -> a -> a) -> a -> [a]
infiniteList start operation changeby =
  let result =
    start : [operation x changeby | x <- result]
  in result

现在你有了一个有用的绑定,result,你可以假设它是完全评估的!在RHS中,你现在基本上已经

start : map (\x -> operation x changeby) result

显然是O(n)

确实有第一个定义,

> infiniteList 1 (*) 2 !! 10000

比我希望等待的时间更长,但是使用修改后的定义,即使在GHCi中也只需要0.04秒。

答案 1 :(得分:2)

运行时间很大程度上取决于GHC如何评估它。

为简化起见,请考虑此版本的功能:

int a f

如果GHC在inf a f = let r = a : [ f x | x <- r ] in r 上执行了常见的子表达式消除,它可以决定对其进行评估,就像它已被写入一样:

<?php

$start = 0;
$end = 1;
$find = 1;

do { 
    $find = rand($start,$end);
    echo $find."<br>";
} while ($start != $find);

if ($start == $find):
    echo "Found!";
endif;

?>

这是在线性时间内运行的。

答案 2 :(得分:1)

我不确定你从哪里获得“批次”的想法。下面是列表前几个元素的成绩单。从那以后,我认为你应该能够找出复杂性。

列表的第一个元素是什么?它是start,因为infiniteList定义为start:[something],并且该表单的任何列表的第一个元素是start

列表的第二个元素是什么?我们当然需要查阅上面列表中的[something]部分。该子列表的第一个元素是operation x changeby,其中xinfiniteList的第一个元素。我们已经确定第一个元素是start,所以第二个元素是operation start changeby,这正是我们想要的。我们需要计算什么来获得第二个元素?只是第一个,加上operation

列表的第三个要素是什么?它是[something]的第二个元素,operation x changeby其中xinfiniteList的第二个元素。幸运的是,我们只计算了那是什么...... 我们需要计算什么来获得第三个元素?只是第一个和第二个,再加上operation

虽然它没有直接回答这个问题,但你应该问问自己,期望这个函数有多复杂。获取n元素需要做多少工作?您在代码中的实现可能会更糟,但它可能会帮助您以不同的方式考虑您的代码。

答案 3 :(得分:1)

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

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

建议,我们需要知道子问题T(n-1),并且完整列表理解具有n-1个操作,然后concat star:...操作有效,并且1计算,所以

T(n) = T(n-1) + (n - 1) + 1 = T(n-1) + n -> O(n^2)

实际上,你可以感受到&#34;通过运行一些例子来说明时间的复杂性。让f n = (infiniteList 0 (+) 1) !! n,然后运行f 10f 100f 1000f 10000,您就可以看到差异。

通常情况下,当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

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

答案 4 :(得分:1)

有时候有助于递归的一种策略是将其扩展几次以更好地了解正在发生的事情。我们试试吧:

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)]]]]]

我们可以看到列表中的第一个元素将按预期变为start。然后第二个元素将从start传递的第一个递归调用中operation x changeby。第三项是什么?那么它将是第一个递归调用的第二项,因此start将通过两次operation x changeby调用。现在模式出现了!一般情况下,infiniteList的第n项将start operation x changeby n-11 + 2 + ... + n - 1 = n(n-1)/2 = O(n^2)。这是非常不幸的,因为正如任何计算机科学专业的学生所知,operation x changeby

当然,有一种更有效的方法来编写这个功能。为了获得第n个项目而不是将start应用于O(n) n-1次,为什么我们不将它只应用到前一个项目?这将为我们提供unfoldr解决方案。例如,我们可以使用Data.List中的import Data.List (unfoldr) infiniteList start operation changeby = unfoldr (\x -> Just (x, operation x changeby)) start

bash-3.2$ curl -i "https://<url>"
curl: (60) SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). The default
 bundle is named curl-ca-bundle.crt; you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.