有许多可行的方法。哪个最适合取决于
如果该算法是众所周知的算法,并且您将其用作子例程,则通常会处于较高级别。如果算法是研究的主要对象,则可能需要更详细地说明。对于分析也可以这样说:如果您需要一个大致的运行时上限,则与需要精确的语句计数时的处理方法不同。
我将为您提供三个著名算法Mergesort的示例,这些示例有望说明这一点。
高水平
Mergesort算法获取一个列表,将其分成两个(大约)等长的部分,在这些部分列表上递归,然后合并(排序的)结果,以便对最终结果进行排序。在单例或空列表上,它返回输入。
该算法显然是正确的排序算法。拆分列表并合并列表可以分别在时间,这使我们可以重现最坏情况下的运行时。根据Master定理,这等于。T (n )= 2 T (nΘ(n)TŤ(n )= 2 吨(n2) +Θ(n)Ť(Ñ )∈ Θ (Ñ 日志n )
中级
Mergesort算法由以下伪代码给出:
procedure mergesort(l : List) {
if ( l.length < 2 ) {
return l
}
left = mergesort(l.take(l.length / 2)
right = mergesort(l.drop(l.length / 2)
result = []
while ( left.length > 0 || right.length > 0 ) {
if ( right.length == 0 || (left.length > 0 && left.head <= right.head) ) {
result = left.head :: result
left = left.tail
}
else {
result = right.head :: result
right = right.tail
}
}
return result.reverse
}
通过归纳证明正确性。对于长度为零或一的列表,该算法非常正确。作为归纳假设,对于某些任意但固定的自然,假设mergesort
在长度最大为列表上正确执行。现在让为长度为的列表。通过归纳假设,并保持(递减)排序的第一个响应版本。递归调用后后半部分。因此,循环在每次迭代中选择尚未调查的最小元素,并将其附加到;因此是一个非递增排序的列表,其中包含和中的所有元素n > 1 公升ñn > 1大号大号大号n + 1left
right
大号while
result
result
left
right
。反向是的非递减排序版本,它是返回的-期望的-结果。大号
对于运行时,让我们计算元素比较和列表操作(渐近地主导运行时)。长度小于2的列表均不会导致这种情况。对于长度为列表,我们进行了一些操作,这些操作是通过为递归调用准备输入而引起的,这些操作来自递归调用本身加上循环和one 。两个递归参数每个最多可以使用列表操作来计算。该循环正好执行了次,每次迭代最多导致一个元素比较和正好两个列表操作。最终可以实现使用n n 2 nn > 1while
reverse
ñwhile
ñreverse
2 n列表操作-将每个元素从输入中删除并放入输出列表中。因此,操作计数满足以下重复发生:
Ť(0 )= T(1 )Ť(n )= 0≤ Ť(⌈ ñ2⌉) +T(⌊ ñ2⌋) +7n
由于明显不减小,因此对于渐近增长,考虑就足够了。在这种情况下,重复发生简化为n = 2 kŤn = 2ķ
Ť(0 )= T(1 )Ť(n )= 0≤ 2 Ť(n2) +7n
通过Master定理,我们得到,它扩展到的运行时。Ť&Element; Θ (ñ 日志n )mergesort
超低水平
考虑一下Isabelle / HOL中的Mergesort的(广义)实现:
types dataset = "nat * string"
fun leq :: "dataset \<Rightarrow> dataset \<Rightarrow> bool" where
"leq (kx::nat, dx) (ky, dy) = (kx \<le> ky)"
fun merge :: "dataset list \<Rightarrow> dataset list \<Rightarrow> dataset list" where
"merge [] b = b" |
"merge a [] = a" |
"merge (a # as) (b # bs) = (if leq a b then a # merge as (b # bs) else b # merge (a # as) bs)"
function (sequential) msort :: "dataset list \<Rightarrow> dataset list" where
"msort [] = []" |
"msort [x] = [x]" |
"msort l = (let mid = length l div 2 in merge (msort (take mid l)) (msort (drop mid l)))"
by pat_completeness auto
termination
apply (relation "measure length")
by simp+
这已经包括明确定义和终止的证明。在此处找到(几乎)完整的正确性证明。
对于“运行时”,即比较次数,可以设置与上一节中类似的重复。除了使用Master定理并忽略常量之外,您还可以对其进行分析,以获得近似渐近等于真实量的近似值。您可以在[1]中找到完整的分析;这是一个粗略的轮廓(不一定适合Isabelle / HOL代码):
如上所述,比较次数的重复次数为
F0= f1个Fñ= 0= f⌈ ñ2⌉+ f⌊ ñ2⌋+ eñ
其中是合并部分结果所需的比较次数²。为了摆脱地板和天花板,我们对是否为偶数进行了区分: ñËññ
{ ˚F2 米F2 米+ 1= 2 f米+ e2 米= f米+ fm + 1+ e2 米+ 1
使用和嵌套前向/后向差异,我们得到FñËñ
∑k = 1n − 1(Ñ - ķ )&CenterDot;&Δ∇ ˚Fķ= fñ− n f1个。
该和与Perron公式的右边匹配。我们定义狄氏产生串联的为Δ∇ ˚Fķ
W(s)=∑k≥1Δ∇fkk−s=11−2−s⋅∑k≥1Δ∇ekks=: ⊟(s)
加上Perron的公式,我们可以
fn=nf1+n2πi∫3−i∞3+i∞⊟(s)ns(1−2−s)s(s+1)ds。
评价取决于被分析这种情况下。除此之外,我们可以-经过一些欺骗之后-应用残差定理来获得⊟(s)
fn∼n⋅log2(n)+n⋅A(log2(n))+1
其中是周期函数,其值在。A[−1,−0.9]
- 梅林变换和渐近性:Flajolet和Golin(1992)的mergesort重现
- 最佳情况:
最坏情况:
平均情况:ËÑ=ñ-1个Ëñ=ñ-en=⌊n2⌋
en=n−1
en=n−⌊n2⌋⌈n2⌉+1−⌈n2⌉⌊n2⌋+1