haskell长度运行时O(1)或O(n)
我当时正在做一项Haskell作业,我在想办法让我的代码更快。 例如,下面的my factors函数查找某个整数的除数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)函数 另外,是否有更好或更有效的方法来编写我的代码?从理论角度来看,我们不知道长
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’