Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/google-chrome/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
haskell长度运行时O(1)或O(n)_Haskell - Fatal编程技术网

haskell长度运行时O(1)或O(n)

haskell长度运行时O(1)或O(n),haskell,Haskell,我当时正在做一项Haskell作业,我在想办法让我的代码更快。 例如,下面的my factors函数查找某个整数的除数 factors :: Int -> Int factors x = length [n | n <- [1..x], mod x n == 0] 我想知道Haskell的length函数是C中类似strlen()的O(n)函数,还是Java中类似String.length()的O(1)函数 另外,是否有更好或更有效的方法来编写我的代码?从理论角度来看,我们不知道长

我当时正在做一项Haskell作业,我在想办法让我的代码更快。 例如,下面的my factors函数查找某个整数的除数

factors :: Int -> Int
factors x = length [n | n <- [1..x], mod x n == 0]
我想知道Haskell的length函数是C中类似strlen()的O(n)函数,还是Java中类似String.length()的O(1)函数


另外,是否有更好或更有效的方法来编写我的代码?

从理论角度来看,我们不知道
长度是否为θ(n),我们知道它是O(n),但从技术上讲,Haskell可能会更快地为已知列表实现它

因为Haskell编译器可以自由地以任何方式实现列表。但是,这并不重要,因为在这种情况下,首先生成列表需要θ(n)

请注意,即使编译器使用更专用的数据结构,Haskell也是懒惰的,因此您对列表的理解并不会产生完整的列表,而是会产生一个可以懒洋洋地生成列表的函数

最后,如果我们急切地评估列表理解,那么它将再次要求O(n)首先生成列表。因此,即使获取长度非常快,生成列表也需要O(n)作为下限。因此,无论
长度
的效率如何,该算法仍将与输入成线性比例

您自己的实现再次使用O(n)(老实说,这不是很安全)。然而,您可以轻松地将数字的因式分解加速到O(sqrt n):

这里我们从1到sqrt(n)进行枚举。每次我们找到一个因子
a
,我们就知道有一个协因子
b=x/a
。只要
a
不等于sqrt(x),我们就知道它们是不同的。如果
a
等于sqrt(x),我们知道
a
等于
b
,因此我们将其计算为一


也就是说,肯定有更快的方法可以做到这一点。这是一个有大量研究的主题,已经产生了更有效的算法。我并不是说上面的方法是最快的,但就时间复杂性而言,这无疑是一个巨大的改进。

用列表理解构建列表已经花费了
O(n)
。因此,当使用在最坏情况下复杂度应为
O(n)
的长度函数时,不会有太多开销

还有,是否有更好或更有效的方法来编写代码

整数分解是最著名的问题之一。尽管我没有足够的专家提出建议(CS.SE即将推出,如果需要的话,可以在这方面提供帮助),但肯定有很多算法被提出。这些方案都不是多项式时间的,但这并不能阻止它们比普通方法更快

即使不看文献,也可以找到一些简单的优化

  • 原始代码扫描整个列表,但这不是必需的。我们可以停在
    sqrt x
    ,因为在那之后就不再有除数了

  • 更重要的是:在我们找到一个除数
    m
    之后,我们可以用
    x
    除以
    m
    (尽可能多次),然后用这个新数字递归。例如,如果在我们尝试
    m=2
    之后
    x=1000
    ,我们计算
    1000->500->250->125
    ,然后在
    125
    中找到新的除数(大于
    2
    )。请注意这是如何使数字小得多的


我将把在Haskell中实现这一策略作为一个练习:-p

在我看来,与公认的答案相反,事实上,您可以通过查看
[a]
的定义来推断
长度(以及许多其他函数)的复杂性:

Prelude> :info []
data [] a = [] | a : [a]    -- Defined in ‘GHC.Types’
列表是归纳定义的;您可以从该定义(几乎只是常规的haskell)中看到,在顶层,列表是构造函数
[]
。显然
length
必须在这个结构上递归
n
次,因此必须是O(n)

能够以这种方式至少凭直觉进行推理是非常重要的,特别是关于无处不在的列表。e、 g.快速
(!!)
的复杂性是什么


如果您想深入研究在存在懒惰的情况下对时间复杂性的正式推理,那么您需要学习Okasaki的“纯功能数据结构”。

但这不是字符串。那么ArrayList.size()如何?关键是复杂性,而不是具体的类型。似乎在编译器优化此代码后,对第一个
因子
表达式的计算根本不涉及构建列表。请记住惰性评估是如何工作的——你不是在“构造一个列表,然后计算它的大小”——你是在计算列表元素生成时计算它们的数量。“从理论角度来看,我们无法知道长度是否为O(n)。因为Haskell编译器可以自由地以任何方式实现列表。”我不认为这取决于编译器。是O(n)。很难想象一个编译器能将其优化到恒定时间。@JeffBurka:我同意这是不太可能的,但请注意,(a)
base
长度的实现;(b)Haskell报告没有谈到如何实现某些数据结构和/或优化它们。我绝对同意这种可能性不大。尽管如此,这并不重要,因为构建列表已经有O(n)个时间复杂性。@Greg:那不是真的<例如,code>x/2
通常更大。关键是如果我们看到
2
是一个因素,我们可以同时计算
2
x/2
。我们不需要调查大于
sqrt(x)
的可能因素,因为一旦我们达到
sqrt(x)
,我们就知道这些因素是什么。@WillemVanOnsem否,
factors :: Int -> Int
factors x = go 1
  where
    go :: Int -> Int
    go i | i2 > x = 0
         | i2 == x = 1
         | mod x i == 0 = 2 + go (i+1)
         | otherwise = go (i + 1)
        where i2 = i*i
Prelude> :info []
data [] a = [] | a : [a]    -- Defined in ‘GHC.Types’